Initial commit
diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..5c6af1a --- /dev/null +++ b/AUTHORS
@@ -0,0 +1,4 @@ +# This file is automatically generated from the git commit history +# by tools/gen_authors.sh. + +Jim Bankoski <jimbankoski@google.com>
diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7a6f995 --- /dev/null +++ b/LICENSE
@@ -0,0 +1,30 @@ +Copyright (c) 2010, Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +
diff --git a/PATENTS b/PATENTS new file mode 100644 index 0000000..4414d83 --- /dev/null +++ b/PATENTS
@@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the WebM Project. + +Google hereby grants to you a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer, and otherwise run, modify and propagate the contents of this +implementation of VP8, where such license applies only to those patent +claims, both currently owned by Google and acquired in the future, +licensable by Google that are necessarily infringed by this +implementation of VP8. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of VP8 or any code incorporated within this +implementation of VP8 constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of VP8 +shall terminate as of the date such litigation is filed.
diff --git a/README b/README new file mode 100644 index 0000000..29b6ac6 --- /dev/null +++ b/README
@@ -0,0 +1,103 @@ +UDPSample +=============== + +This sample illustrates using vp8 for a live video transmission over lossy +UDP with a back channel. + +It includes two sample console based subprograms: + + +GrabCompressAndSend +upon receipt of session initiation requests from ReceiveDecompressAndPlay, +captures live video from an installed direct show camera, compresses it, +packetizes it, adds forward error correction (if specified) and then sends +the data across a port using UDP. Upon any receipt of a request for recovery +or for a packet resend it responds appropriately. + +ReceiveDecompressAndPlay +initiates a session with GrabCompressAndSend sending parameters regarding +forward error correction, simulated packetloss, datarate, frame size, etc. +The program then awaits arrival of packets from GrabCompressAndSend and uses +them to reassemble frames, which it then decompress and plays for the user. +Any lost packets that can be rebuilt using forward error correction are +rebuilt. If a packet is lost and can't be rebuilt, packet resend requests +are sent. If there is still not a timely response or a large number of +packets are lost the program requests a recovery frame. + +Build requirements : + +Linux -> 64-bit, libsdl and libvpx must be installed and in your paths. +MacOSX -> libsdk, libvpx and libvidcap must be installed and in your paths. +Windows -> VS2005, PlatformSDK ( + DirectShow Baseclasses ) in include and lib + path, DirectX in include and libpath + +DISCLAIMER: This sample code has only been tested on a very limited set of +machines - we can use your help to expand and make it work on a wider range +of platforms. + + + +============================================================ +Usage: Open up two command prompt windows. in one run : + +grabcompressandsend + +in the other run + +receivedecompressandplay + +Two video windows should open and video should be played. + + +On 2 separate computers the following command lines requests 400 kbps +960x720 video compressed at 15fps, to be sent with 1 xor-packet created from +every 5 packets over ports 1407 and 1408. Recovery frames are requested if a +lost packet ages to 800 ms. + +receivedecompressandplay -w 960 -h 720 -f 15 -b 400 -n 6 -d 5 -t 800 -s 1408 -r 1407 + +grabcompressandsend -i 10.10.28.1 -s 1407 -r 1408 + +============================================================ + +ReceiveDecompressAndPlay has the following other options: + +-w [640] request capture width +-h [480] request capture height +-f [30] request capture frame rate +-b [300] videoBitrate = ato +-n [6] fecNumerator ( redundancy numerator) +-d [5] fecDenominator ( redundancy denominator) + 6/5 means 1 xor packet for every 5 packets, + 4/1 means 3 duplicate packets for every packet +-t [800] milliseconds before giving up and requesting recovery +-i [50] time in milliseconds between attempts at a packet resend +-c [12] number of lost packets before requesting recovery +-l [0] packets to lose out of every 1000 +-s [1408] port to send requests to +-r [1407] port to receive requests on. + + +GrabCompressAndSend has the following options: + +-i [127.0.0.1] Port to send data to. +-s [1408] port to send requests to +-r [1407] port to receive requests on. + + + +Caveats: This is just sample code. There are many problems that this +code does not make any attempt at all to resolve. Ie. Getting through +firewalls, sensible session initiation (IE SIP), responding to datarate +or packetloss fluctuation, keeping audio and video and synch. Nor does it +resolve issues like long term dropouts or other common problems in any +ensible way. The handling of packets and skipped packets is rudimentary +with extra copies, and a very rudimentary way of handling skips. The program +requires directx7 or better with yv12 offscreen surfaces for decode, and a web +camera / capture device capable of supplying i420. Basic RTP is used but RTCP +isn't. + + + + +
diff --git a/VS2005WS/UDPSAMPLE.sln b/VS2005WS/UDPSAMPLE.sln new file mode 100644 index 0000000..f90f31c --- /dev/null +++ b/VS2005WS/UDPSAMPLE.sln
@@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UDPSAMPLE", "UDPSAMPLE.vcproj", "{1BA1039F-4A01-47EC-85C6-1EB7861B95ED}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + GrabCompressAndSend|Win32 = GrabCompressAndSend|Win32 + ReceiveDecompressAndPlay|Win32 = ReceiveDecompressAndPlay|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1BA1039F-4A01-47EC-85C6-1EB7861B95ED}.GrabCompressAndSend|Win32.ActiveCfg = GrabCompressAndSend|Win32 + {1BA1039F-4A01-47EC-85C6-1EB7861B95ED}.GrabCompressAndSend|Win32.Build.0 = GrabCompressAndSend|Win32 + {1BA1039F-4A01-47EC-85C6-1EB7861B95ED}.ReceiveDecompressAndPlay|Win32.ActiveCfg = ReceiveDecompressAndPlay|Win32 + {1BA1039F-4A01-47EC-85C6-1EB7861B95ED}.ReceiveDecompressAndPlay|Win32.Build.0 = ReceiveDecompressAndPlay|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/VS2005WS/UDPSAMPLE.suo b/VS2005WS/UDPSAMPLE.suo new file mode 100644 index 0000000..7f9f772 --- /dev/null +++ b/VS2005WS/UDPSAMPLE.suo Binary files differ
diff --git a/VS2005WS/UDPSAMPLE.vcproj b/VS2005WS/UDPSAMPLE.vcproj new file mode 100644 index 0000000..42714df --- /dev/null +++ b/VS2005WS/UDPSAMPLE.vcproj
@@ -0,0 +1,283 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="UDPSAMPLE" + ProjectGUID="{1BA1039F-4A01-47EC-85C6-1EB7861B95ED}" + RootNamespace="UDPSAMPLE" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="GrabCompressAndSend|Win32" + OutputDirectory="$(SolutionDir)" + IntermediateDirectory="$(SolutionDir)" + ConfigurationType="1" + UseOfMFC="2" + UseOfATL="2" + CharacterSet="2" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;WINDOWS;_CRT_SECURE_NO_WARNINGS" + MinimalRebuild="true" + BasicRuntimeChecks="0" + RuntimeLibrary="3" + BufferSecurityCheck="false" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="false" + DebugInformationFormat="1" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalOptions="/force" + AdditionalDependencies="vpxmd.lib Ws2_32.lib " + OutputFile="$(OutDir)\grabcompressandsend.exe" + LinkIncremental="2" + IgnoreAllDefaultLibraries="false" + IgnoreDefaultLibraryNames="MSVCRT.lib" + GenerateDebugInformation="false" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="ReceiveDecompressAndPlay|Win32" + OutputDirectory="$(SolutionDir)" + IntermediateDirectory="$(SolutionDir)" + ConfigurationType="1" + UseOfMFC="2" + UseOfATL="2" + CharacterSet="2" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;WINDOWS;_CRT_SECURE_NO_WARNINGS" + MinimalRebuild="true" + BasicRuntimeChecks="0" + RuntimeLibrary="3" + BufferSecurityCheck="false" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="false" + DebugInformationFormat="0" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalOptions="/force" + AdditionalDependencies="vpxmd.lib Ws2_32.lib ddraw.lib dxguid.lib gdi32.lib " + OutputFile="$(OutDir)\receivedecompressandplay.exe" + LinkIncremental="2" + IgnoreAllDefaultLibraries="false" + IgnoreDefaultLibraryNames="MSVCRT.lib" + GenerateDebugInformation="false" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath="..\grabcompressandsend.cpp" + > + <FileConfiguration + Name="ReceiveDecompressAndPlay|Win32" + ExcludedFromBuild="true" + > + <Tool + Name="VCCLCompilerTool" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\receivedecompressandplay.cpp" + > + <FileConfiguration + Name="GrabCompressAndSend|Win32" + ExcludedFromBuild="true" + > + <Tool + Name="VCCLCompilerTool" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\stdafx.cpp" + > + </File> + <File + RelativePath="..\time.c" + > + </File> + <File + RelativePath="..\vpx_network.c" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\qedit.h" + > + </File> + <File + RelativePath="..\rtp.h" + > + </File> + <File + RelativePath="..\stdafx.h" + > + </File> + <File + RelativePath="..\tcdebug.h" + > + </File> + <File + RelativePath="..\tctypes.h" + > + </File> + <File + RelativePath="..\uvc_compat.h" + > + </File> + <File + RelativePath="..\uvcvideo.h" + > + </File> + <File + RelativePath="..\vpx_mem.h" + > + </File> + <File + RelativePath="..\vpx_network.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/grabcompressandsend.cpp b/grabcompressandsend.cpp new file mode 100644 index 0000000..f6408de --- /dev/null +++ b/grabcompressandsend.cpp
@@ -0,0 +1,1161 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +/* + * This example illustrates using VP8 in a packet loss scenario by xmitting + * video over UDP with Forward Error Correction, Packet Resend, and + * some Unique VP8 functionality. + * + */ + +#include "vpx_network.h" + +#include <stdio.h> +#include <stdarg.h> +#include <stdio.h> +#include <ctype.h> //for tolower +#include <string.h> + + +extern "C" +{ +#include "rtp.h" +#include "vpx/vpx_encoder.h" +#include "vpx/vp8cx.h" +} + +unsigned int drop_first = 60; +unsigned int count_captured_frames = 0; + +const int size_buffer = 1680; +#define FAIL_ON_NONZERO(x) if((x)) { vpxlog_dbg(ERRORS,#x"\n");return -1; }; +#define FAIL_ON_ZERO(x) if(!(x)) { vpxlog_dbg(ERRORS,#x"\n");return -1; }; +#define FAIL_ON_NEGATIVE(x) if((x)<0) { vpxlog_dbg(ERRORS,#x"\n");return -1; }; + +#ifdef WINDOWS +#include "stdafx.h" +#include <dshow.h> +#include <atlcomcli.h> +#include <atlbase.h> +#include "qedit.h" +#pragma comment(lib, "strmiids.lib") + +#include "tctypes.h" +#include <conio.h> +#include <vfw.h> + +GUID MEDIASUBTYPE_I420 = +{ + MAKEFOURCC('I', '4', '2', '0'), + 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 +}; + +CComPtr<IGraphBuilder> graph; +CComPtr<IBaseFilter> capture, null_filter, grabber, video_filter; +CComPtr<IPin> cap_out_pin, grab_in_pin, grab_out_pin, null_in_pin, null_out_pin; +CComPtr<ISampleGrabber> sample_grabber; +CComPtr<IAMStreamConfig> config; +CComQIPtr<IMediaControl, &IID_IMediaControl> control; +CComPtr<IVideoWindow> video_window; +#else +#define Sleep usleep +extern "C" int _kbhit(void); +#endif + +vpx_image_t raw; +bool buffer_has_frame = false; +double buffer_time; +long long last_time_in_nanoseconds = 0; + +int display_width = 640; +int display_height = 480; +int capture_frame_rate = 30; +int video_bitrate = 400; +int fec_numerator = 6; +int fec_denominator = 5; +unsigned short send_port = 1407; +unsigned short recv_port = 1408; + +#define PS 2048 +#define PSM (PS-1) +#define MAX_NUMERATOR 16 + +typedef enum +{ + NONE, + XOR, + RS +} FEC_TYPE; + +typedef struct +{ + unsigned int size; + FEC_TYPE fecType; + unsigned int fec_numerator; + unsigned int fec_denominator; + unsigned int new_fec_denominator; + unsigned int count; + unsigned int add_ptr; + unsigned int send_ptr; + unsigned int max; + unsigned int fec_count; + unsigned short seq; + PACKET packet[PS]; +} PACKETIZER; + +PACKETIZER x; +tc8 one_packet[8000]; + +unsigned char output_video_buffer[1280*1024*3]; + +#ifdef WINDOWS +HRESULT FindFilter(CLSID cls, IBaseFilter **pp, bool name, CComVariant &filter) +{ + HRESULT hr; + CComPtr<ICreateDevEnum> dev_enum; + hr = dev_enum.CoCreateInstance(CLSID_SystemDeviceEnum); + + if (!SUCCEEDED(hr)) + return hr; + + CComPtr<IEnumMoniker> enum_moniker; + hr = dev_enum->CreateClassEnumerator(cls, &enum_moniker, 0); + + if (!SUCCEEDED(hr)) + return hr; + + CComPtr<IMoniker> moniker; + ULONG fetched; + + while (enum_moniker->Next(1, &moniker, &fetched) == S_OK) + { + CComPtr<IPropertyBag> iPropBag; + hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&iPropBag); + + if (SUCCEEDED(hr)) + { + // To retrieve the filter's friendly name, do the following: + CComVariant var_name; + hr = iPropBag->Read(L"FriendlyName", &var_name, 0); + + if (SUCCEEDED(hr)) + { + if (!name || var_name == filter) + { + // To create an instance of the filter, do the following: + hr = moniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void **)pp); + break; + } + } + } + + moniker = NULL; + } + + return hr; +} + +HRESULT GetFirstPin(IBaseFilter *p, PIN_DIRECTION pd, IPin **pp) +{ + CComPtr<IEnumPins> enum_moniker; + p->EnumPins(&enum_moniker); + CComPtr<IPin> pin; + + while (enum_moniker && enum_moniker->Next(1, &pin, NULL) == S_OK) + { + PIN_INFO pi; + pin->QueryPinInfo(&pi); + + if (pi.dir == pd) + { + *pp = pin.Detach(); + return S_OK; + } + + pin = NULL; + } + + return E_FAIL; +} +class CVideoCallback : public ISampleGrabberCB +{ +public: + FILE *output_file; + STDMETHODIMP_(ULONG) AddRef() + { + return 2; + } + STDMETHODIMP_(ULONG) Release() + { + return 1; + } + STDMETHODIMP QueryInterface(REFIID riid, void **ppv) + { + if (riid == IID_ISampleGrabberCB || riid == IID_IUnknown) + { + *ppv = (void *)static_cast<ISampleGrabberCB *>(this); + return NOERROR; + } + + return E_NOINTERFACE; + } + CVideoCallback(char *filename, int decimate) + { +// output_file=fopen(filename,"wb"); + } + ~CVideoCallback() + { +// fclose(output_file); + } + STDMETHODIMP SampleCB(double sample_time, IMediaSample *sample) + { + return S_OK; + } + STDMETHODIMP BufferCB(double sample_time, BYTE *buffer, long buffer_len) + { + + buffer_has_frame = false; + memcpy(raw.img_data, buffer, buffer_len); + buffer_has_frame = true; + buffer_time = sample_time; + //fwrite(inputVideoBuffer,buffer_len,sizeof(BYTE),output_file); + return S_OK; + } +}; +CVideoCallback video_callback("demo.yv12", 0); +CComQIPtr<ISampleGrabberCB, &IID_ISampleGrabberCB> video_callback_ptr(&video_callback); +#define HRE(y) if(FAILED(hr=y)) {vpxlog_dbg(FRAME,#y##":%x\n",hr);return hr;}; + +int start_capture(void) +{ + HRESULT hr; + + vpxlog_dbg(FRAME, "Creating filters...\n"); + HRE(graph.CoCreateInstance(CLSID_FilterGraph)); + HRE(FindFilter(CLSID_VideoInputDeviceCategory, &capture, false, CComVariant(L""))); + HRE(CoCreateInstance(CLSID_SampleGrabber, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void **>(&grabber))); + HRE(grabber->QueryInterface(IID_ISampleGrabber, (void **)&sample_grabber)); + sample_grabber->SetBufferSamples(true); + HRE(CoCreateInstance(CLSID_VideoRenderer, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void **>(&video_filter))); + HRE(graph->AddFilter(capture, L"Capture")); + HRE(graph->AddFilter(grabber, L"SampleGrabber")); + HRE(graph->AddFilter(video_filter, L"Video Renderer")); + + vpxlog_dbg(FRAME, "Getting pins...\n"); + HRE(GetFirstPin(capture, PINDIR_OUTPUT, &cap_out_pin)); + HRE(grabber->FindPin(L"In", &grab_in_pin)); + HRE(grabber->FindPin(L"Out", &grab_out_pin)); + HRE(video_filter->FindPin(L"In", &null_in_pin)); + + vpxlog_dbg(FRAME, "Connecting pins...\n"); + AM_MEDIA_TYPE pmt; + ZeroMemory(&pmt, sizeof(AM_MEDIA_TYPE)); + pmt.subtype = MEDIASUBTYPE_I420; + pmt.majortype = MEDIATYPE_Video; + pmt.formattype = FORMAT_VideoInfo; + pmt.pbFormat = reinterpret_cast<BYTE *>(CoTaskMemAlloc(sizeof(VIDEOINFOHEADER))); + ZeroMemory(pmt.pbFormat, sizeof(VIDEOINFOHEADER)); + pmt.cbFormat = sizeof(VIDEOINFOHEADER); + pmt.bFixedSizeSamples = 1; + pmt.bTemporalCompression = 0; + + VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER *>(pmt.pbFormat); + pVih->bmiHeader.biCompression = mmioFOURCC('I', '4', '2', '0'); + pVih->bmiHeader.biWidth = display_width; + pVih->bmiHeader.biHeight = display_height; + pVih->bmiHeader.biBitCount = 12; + pVih->bmiHeader.biPlanes = 3; + pVih->bmiHeader.biSizeImage = pVih->bmiHeader.biWidth * pVih->bmiHeader.biHeight * pVih->bmiHeader.biBitCount / 8; + pVih->AvgTimePerFrame = 10000000 / capture_frame_rate; + pVih->rcSource.top = 0; + pVih->rcSource.left = 0; + pVih->rcSource.bottom = display_height; + pVih->rcSource.right = display_width; + pVih->rcTarget.top = 0; + pVih->rcTarget.left = 0; + pVih->rcTarget.bottom = display_height; + pVih->rcTarget.right = display_width; + pmt.lSampleSize = pVih->bmiHeader.biSizeImage; + + HRE(cap_out_pin->QueryInterface(IID_IAMStreamConfig, (void **)&config)); + HRE(config->SetFormat(&pmt)); + AM_MEDIA_TYPE *amt; + HRE(config->GetFormat(&amt)); + VIDEOINFOHEADER *pVih2 = reinterpret_cast<VIDEOINFOHEADER *>(amt->pbFormat); + + HRE(graph->Connect(cap_out_pin, grab_in_pin)); + HRE(graph->Connect(grab_out_pin, null_in_pin)); + HRE(video_filter->QueryInterface(IID_IVideoWindow, (void **)&video_window)); + HRE(sample_grabber->SetCallback(video_callback_ptr, 1)); + + vpxlog_dbg(FRAME, "Running graph...\n"); + control = graph; + + HRE(control->Run()); + return 0; +} + +int get_frame(void) +{ + if (buffer_has_frame) + { + buffer_time = get_time() / 1000.000; + return 0; + } + else + return -1; +} +#else + +#ifdef MACOSX + +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <time.h> +#include <sys/time.h> + +#include <vidcap/vidcap.h> + + +struct my_source_context +{ + vidcap_src *src; + char name[VIDCAP_NAME_LENGTH]; +}; + + +char frame[1280*720*3/2]; + +int get_frame(void) +{ + if (buffer_has_frame) + { + buffer_time = get_time() / 1000.000; + return 0; + } + else + return -1; +} + +static int user_capture_callback(vidcap_src *src, + void *user_data, + struct vidcap_capture_info *cap_info) +{ + memcpy(raw.img_data, cap_info->video_data, display_width * display_height * 3 / 2); + + buffer_has_frame = 1; + + return 0; +} +const int sleep_ms = 10000; +vidcap_state *vc; +vidcap_sapi *sapi; + +struct vidcap_sapi_info sapi_info; +struct vidcap_src_info *src_list; +int src_list_len; +struct my_source_context *ctx_list; +int start_capture(void) +{ + int i; + + FAIL_ON_ZERO(vc = vidcap_initialize()); + FAIL_ON_ZERO(sapi = vidcap_sapi_acquire(vc, 0)); + + FAIL_ON_NONZERO(vidcap_sapi_info_get(sapi, &sapi_info)); + + src_list_len = vidcap_src_list_update(sapi); + + if (src_list_len < 0) + { + vpxlog_dbg(ERRORS, "failed vidcap_src_list_update()\n"); + return -1; + } + else if (src_list_len == 0) + { + vpxlog_dbg(ERRORS, "no sources available\n"); + return -1; + } + + FAIL_ON_ZERO(src_list = (struct vidcap_src_info *) calloc(src_list_len, + sizeof(struct vidcap_src_info))) + + FAIL_ON_NONZERO(vidcap_src_list_get(sapi, src_list_len, src_list)); + FAIL_ON_ZERO(ctx_list = (my_source_context *) calloc(src_list_len, sizeof(*ctx_list))) + + for (i = 0; i < src_list_len; ++i) + { + struct vidcap_fmt_info fmt_info; + ctx_list[i].src = vidcap_src_acquire(sapi, &src_list[i]); + fmt_info.width = display_width; + fmt_info.height = display_height; + fmt_info.fps_numerator = capture_frame_rate; + fmt_info.fps_denominator = 1; + fmt_info.fourcc = 100; // i420 + + FAIL_ON_NONZERO(vidcap_format_bind(ctx_list[i].src, &fmt_info)); + FAIL_ON_NONZERO(vidcap_format_info_get(ctx_list[i].src, &fmt_info)); + + sprintf(ctx_list[i].name, "source %d", i); + + FAIL_ON_NONZERO(vidcap_src_capture_start(ctx_list[i].src, + user_capture_callback, + &ctx_list[i])) + } + + free(src_list); + return 0; + +} +int stop_capture(void) +{ + int i; + + for (i = 0; i < src_list_len; ++i) + { + if (!ctx_list[i].src) + continue; + + FAIL_ON_NONZERO(vidcap_src_capture_stop(ctx_list[i].src)) + FAIL_ON_NONZERO(vidcap_src_release(ctx_list[i].src)) + } + + free(ctx_list); + FAIL_ON_NONZERO(vidcap_sapi_release(sapi)) + + vidcap_destroy(vc); + + return 0; +} + +#else +#include <iostream> +#include <fcntl.h> +#include <sys/mman.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "uvcvideo.h" +#include <sys/ioctl.h> +#include <sys/select.h> +#define NB_BUFFER 4 + +using namespace std; +char frame[1280*720*3/2]; + +int uyvy2yv12(char *uyvy, int w, int h) +{ + unsigned char *y = raw.img_data; + unsigned char *u = w * h + y; + unsigned char *v = w / 2 * h / 2 + u; + int i, j; + + char *p = uyvy; + + // pretty clearly a very slow way to do this even in c + // super easy simd conversion + for (; y < u; p += 4) + { + *y++ = p[0]; + *y++ = p[2]; + } + + p = uyvy; + + for (i = 0; i<(h >> 1); i++, p += (w << 1)) + for (j = 0; j<(w >> 1); j++, p += 4) + * u++ = p[1]; + + p = uyvy; + + for (i = 0; i<(h >> 1); i++, p += (w << 1)) + for (j = 0; j<(w >> 1); j++, p += 4) + * v++ = p[3]; + + return 0; +} + +struct v4l2_capability cap; +struct v4l2_format fmt; +struct v4l2_buffer buf; +struct v4l2_requestbuffers rb; +void *mem[NB_BUFFER]; +int fd; +int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + +int start_capture(void) +{ + + FAIL_ON_NEGATIVE(fd = open("/dev/video0", O_RDWR | O_NONBLOCK)) + + memset(&cap, 0, sizeof(struct v4l2_capability)); + + FAIL_ON_NEGATIVE(ioctl(fd, VIDIOC_QUERYCAP, &cap)) + + memset(&fmt, 0, sizeof(struct v4l2_format)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = display_width; + fmt.fmt.pix.height = display_height; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + fmt.fmt.pix.field = V4L2_FIELD_ANY; + + FAIL_ON_NEGATIVE(ioctl(fd, VIDIOC_S_FMT, &fmt)) + + struct v4l2_streamparm setfps; + + memset(&setfps, 0, sizeof(struct v4l2_streamparm)); + setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + setfps.parm.capture.timeperframe.numerator = 1; + setfps.parm.capture.timeperframe.denominator = capture_frame_rate; + + FAIL_ON_NONZERO(ioctl(fd, VIDIOC_S_PARM, &setfps)) + + memset(&rb, 0, sizeof(struct v4l2_requestbuffers)); + rb.count = NB_BUFFER; + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rb.memory = V4L2_MEMORY_MMAP; + + FAIL_ON_NONZERO(ioctl(fd, VIDIOC_REQBUFS, &rb)) + + int i; + + /* map the buffers */ + for (i = 0; i < NB_BUFFER; i++) + { + memset(&buf, 0, sizeof(struct v4l2_buffer)); + buf.index = i; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + + FAIL_ON_NEGATIVE(ioctl(fd, VIDIOC_QUERYBUF, &buf)) + + mem[i] = mmap(0 , buf.length, PROT_READ, MAP_SHARED, fd, buf.m.offset); + + if (mem[i] == MAP_FAILED) + { + cout << "Error mapping buffers." << endl; + return -1; + } + } + + /* Queue the buffers. */ + for (i = 0; i < NB_BUFFER; ++i) + { + memset(&buf, 0, sizeof(struct v4l2_buffer)); + buf.index = i; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + + FAIL_ON_NEGATIVE(ioctl(fd, VIDIOC_QBUF, &buf)) + } + + // start streaming + FAIL_ON_NEGATIVE(ioctl(fd, VIDIOC_STREAMON, &type)) + return -1; +} + +int get_frame(void) +{ + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + + // get a frame if we can + if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) + return -1; + + // super ugly conversion :) + if (buf.bytesused > 0) + { + uyvy2yv12((char *) mem[buf.index], display_width, display_height); + //fwrite(frame, w*h*3/2, 1, captureFile); + } + + // put the buffer back + FAIL_ON_NEGATIVE(ioctl(fd, VIDIOC_QBUF, &buf)) + + buffer_time = get_time() / 1000.000; + + if (count_captured_frames++ < drop_first) + return -1; + + return 0; +} + +int stop_capture(void) +{ + //fclose(captureFile); + FAIL_ON_NEGATIVE(ioctl(fd, VIDIOC_STREAMOFF, &type)) + + return 0; +} + +#endif +#endif + +int create_packetizer( + PACKETIZER *x, + FEC_TYPE fecType, + unsigned int fec_numerator, + unsigned int fec_denominator +) +{ + x->size = PACKET_SIZE; + x->fecType = fecType; + x->fec_numerator = fec_numerator; + x->fec_denominator = fec_denominator; + x->new_fec_denominator = fec_denominator; + x->max = PS; + x->count = 0; + x->add_ptr = 0; + x->send_ptr = 0; + x->fec_count = x->fec_denominator; + + x->seq = 7; + x->send_ptr = x->add_ptr = (x->seq & PSM); + return 0; // SUCCESS +} + +int make_redundant_packet +( + PACKETIZER *p, + unsigned int end_frame, + unsigned int time, + unsigned int frametype +) +{ + long long *in[MAX_NUMERATOR]; + long long *out = (long long *) p->packet[p->add_ptr].data; + unsigned int i, j; + unsigned int max_size = 0; + unsigned int max_round; + + // make a number of exact duplicates of this packet + if (p->fec_denominator == 1) + { + int dups = p->fec_numerator - p->fec_denominator ; + void *duplicand = (void *) &p->packet[(p->add_ptr-1)&PSM]; + + while (dups) + { + memcpy((void *) &p->packet[p->add_ptr], duplicand, sizeof(PACKET)); + dups --; + p->add_ptr++; + p->add_ptr &= PSM; + } + + p->fec_denominator = p->new_fec_denominator; + p->fec_count = p->fec_denominator; + p->count ++; + return 0; + } + + p->packet[p->add_ptr].timestamp = time; + p->packet[p->add_ptr].seq = p->seq; + p->packet[p->add_ptr].size = max_size; + p->packet[p->add_ptr].type = XORPACKET; + p->packet[p->add_ptr].redundant_count = p->fec_denominator; + p->packet[p->add_ptr].new_frame = 0; + p->packet[p->add_ptr].end_frame = end_frame; + p->packet[p->add_ptr].frame_type = frametype; + + // find address of last denominator packets data store in in ptr + for (i = 0; i < p->fec_denominator; i++) + { + int ptr = ((p->add_ptr - i - 1)&PSM); + in[i] = (long long *) p->packet[ptr].data;; + max_size = (max_size > p->packet[ptr].size ? max_size : p->packet[ptr].size); + } + + // go through a full packet size + max_round = (max_size + sizeof(long long) - 1) / sizeof(long long); + + for (j = 0; j < max_round; j++) + { + // start with the most recent packet + *out = *(in[0]); + + // xor all the older packets with out + for (i = 1; i < p->fec_denominator; i++) + { + *out ^= *(in[i]); + in[i]++; + } + + in[0]++; + out++; + } + + p->seq ++; + + // move to the next packet + p->add_ptr ++; + p->add_ptr &= PSM; + + // add one to our packet count + p->count ++; + + if (p->count > p->max) + return -1; // filled up our packet buffer + + p->fec_denominator = p->new_fec_denominator; + p->fec_count = p->fec_denominator; + return 0; +} + +int packetize +( + PACKETIZER *p, + unsigned int time, + unsigned char *data, + unsigned int size, + unsigned int frame_type +) +{ + int new_frame = 1; + + // more bytes to copy around + while (size > 0) + { + unsigned int psize = (p->size < size ? p->size : size); + p->packet[p->add_ptr].timestamp = time; + p->packet[p->add_ptr].seq = p->seq; + p->packet[p->add_ptr].size = psize; + p->packet[p->add_ptr].type = DATAPACKET; + + if (p->fec_denominator == 1) + p->packet[p->add_ptr].redundant_count = 2; + else + p->packet[p->add_ptr].redundant_count = p->fec_count; + + p->packet[p->add_ptr].new_frame = new_frame; + p->packet[p->add_ptr].frame_type = frame_type; + //vpxlog_dbg(SKIP, "%c", (frame_type==NORMAL?'N':'O')); + + new_frame = 0; + + memcpy(p->packet[p->add_ptr].data, data, psize); + + // make sure rest of packet is 0'ed out for redundancy if necessary. + if (size < p->size) + memset(p->packet[p->add_ptr].data + psize, 0, p->size - psize); + + data += psize; + size -= psize; + p->packet[p->add_ptr].end_frame = (size == 0); + + p->seq ++; + p->add_ptr ++; + p->add_ptr &= PSM; + + p->count ++; + + if (p->count > p->max) + return -1; // filled up our packet buffer + + // time for redundancy? + p->fec_count --; + + if (!p->fec_count) + make_redundant_packet(p, (size == 0), time, frame_type); + } + + return 0; +} +#define WRITEFILE +//#define ONEWAY +int send_packet(PACKETIZER *p, struct vpxsocket *vpxSock, union vpx_sockaddr_x address) +{ + TCRV rc; + tc32 bytes_sent; + + if (p->send_ptr == p->add_ptr) + return -1; + + p->packet[p->send_ptr].ssrc = 411; + vpxlog_dbg(LOG_PACKET, "Sent Packet %d, %d, %d : new=%d \n", p->packet[p->send_ptr].seq, p->packet[p->send_ptr].timestamp, p->packet[p->send_ptr].frame_type, p->packet[p->send_ptr].new_frame); +#ifndef ONEWAY + rc = vpx_net_sendto(vpxSock, (tc8 *) &p->packet[p->send_ptr], PACKET_HEADER_SIZE + p->packet[p->send_ptr].size, &bytes_sent, address); +#endif + p->send_ptr ++; + p->send_ptr &= PSM; + p->count --; + + return 0; +} +void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s) +{ + if (ctx->err) + { + vpxlog_dbg(FRAME, "%s: %s\n", s, vpx_codec_error(ctx)); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char *argv[]) +{ + char ip[512]; + int flags = 0; + strncpy(ip, "127.0.0.1", 512); + printf("GrabCompressAndSend: (-? for help) \n"); + + vpx_codec_enc_cfg_t cfg; + + vpx_codec_enc_config_default(&vpx_codec_vp8_cx_algo, &cfg, 0); + cfg.rc_target_bitrate = video_bitrate; + cfg.g_w = display_width; + cfg.g_h = display_height; + cfg.g_timebase.num = 1; + cfg.g_timebase.den = (int) 10000000; + cfg.rc_end_usage = VPX_CBR; + cfg.g_pass = VPX_RC_ONE_PASS; + cfg.g_lag_in_frames = 0; + cfg.rc_min_quantizer = 20; + cfg.rc_max_quantizer = 50; + cfg.rc_dropframe_thresh = 1; + cfg.rc_buf_optimal_sz = 1000; + cfg.rc_buf_initial_sz = 1000; + cfg.rc_buf_sz = 1000; + cfg.g_error_resilient = 1; + cfg.kf_mode = VPX_KF_DISABLED; + cfg.kf_max_dist = 999999; + cfg.g_threads = 1; + + + int cpu_used = 8; + int static_threshold = 1200; + + while (--argc > 0) + { + if (argv[argc][0] == '-') + { + switch (argv[argc][1]) + { + case 'm': + case 'M': + cfg.rc_dropframe_thresh = atoi(argv[argc--+1]); + break; + case 'c': + case 'C': + cpu_used = atoi(argv[argc--+1]); + break; + case 't': + case 'T': + static_threshold = atoi(argv[argc--+1]); + break; + case 'b': + case 'B': + cfg.rc_min_quantizer = atoi(argv[argc--+1]); + break; + case 'q': + case 'Q': + cfg.rc_max_quantizer = atoi(argv[argc--+1]); + break; + case 'd': + case 'D': + drop_first = atoi(argv[argc--+1]); + break; + case 'i': + case 'I': + strncpy(ip, argv[argc--+1], 512); + break; + case 's': + case 'S': + send_port = atoi(argv[argc--+1]); + break; + case 'r': + case 'R': + recv_port = atoi(argv[argc--+1]); + break; + default: + printf( + "========================: \n" + "Captures, compresses and sends video to ReceiveDecompressand play sample\n\n" + "-m [1] buffer level at which to drop frames 0 shuts it off \n" + "-c [12] amount of cpu to leave free of 16 \n" + "-t [1200] sad score below which is just a copy \n" + "-b [20] minimum quantizer ( best frame quality )\n" + "-q [52] maximum frame quantizer ( worst frame quality ) \n" + "-d [60] number of frames to drop at the start\n" + "-i [127.0.0.1] Port to send data to. \n" + "-s [1408] port to send requests to\n" + "-r [1407] port to receive requests on. \n" + "\n"); + exit(0); + break; + } + } + } + + struct vpxsocket vpx_socket, vpx_socket2; + + union vpx_sockaddr_x address, address2; + + TCRV rc; + + int i; + + int bytes_read; + +#ifdef WINDOWS + HRESULT hr; + + HRE(CoInitialize(NULL)); + +#endif + +#ifdef WRITEFILE + FILE *out_file = fopen("test.vpx", "wb"); + +#endif + + int request_recovery = 0; + + int gold_recovery_seq = 0; + + int altref_recovery_seq = 0; + + unsigned int recovery_flags[] = + { + 0, // NORMAL, + VPX_EFLAG_FORCE_KF, // KEY, + VP8_EFLAG_FORCE_GF | VP8_EFLAG_NO_UPD_ARF | + VP8_EFLAG_NO_REF_LAST | VP8_EFLAG_NO_REF_ARF, // GOLD = 2, + VP8_EFLAG_FORCE_ARF | VP8_EFLAG_NO_UPD_GF | + VP8_EFLAG_NO_REF_LAST | VP8_EFLAG_NO_REF_GF // ALTREF = 3 + }; + + vpx_net_init(); + + // data send socket + FAIL_ON_NONZERO(vpx_net_open(&vpx_socket, vpx_IPv4, vpx_UDP)) + FAIL_ON_NONZERO(vpx_net_get_addr_info(ip, send_port, vpx_IPv4, vpx_UDP, &address)) + // feedback socket + FAIL_ON_NONZERO(vpx_net_open(&vpx_socket2, vpx_IPv4, vpx_UDP)) + vpx_net_set_read_timeout(&vpx_socket2, 0); + + rc = vpx_net_bind(&vpx_socket2, 0 , recv_port); + + vpx_net_set_send_timeout(&vpx_socket, vpx_NET_NO_TIMEOUT); + + // make sure 2 way discussion taking place before getting started + + int bytes_sent; + +#ifndef ONEWAY + + while (!_kbhit()) + { + char init_packet[PACKET_SIZE] = "initiate call"; + rc = vpx_net_sendto(&vpx_socket, (tc8 *) &init_packet, PACKET_SIZE , &bytes_sent, address); + Sleep(200); + + rc = vpx_net_recvfrom(&vpx_socket2, one_packet, sizeof(one_packet), &bytes_read, &address2); + + if (rc != TC_OK && rc != TC_WOULDBLOCK) + vpxlog_dbg(LOG_PACKET, "error\n"); + + if (bytes_read == -1) + bytes_read = 0; + + if (bytes_read) + { + if (strncmp(one_packet, "configuration ", 14) == 0) + { + sscanf(one_packet + 14, "%d %d %d %d %d %d", &display_width, &display_height, &capture_frame_rate, &video_bitrate, &fec_numerator, &fec_denominator); + printf("Dimensions: %dx%-d %dfps %dkbps %d/%dFEC\n", display_width, display_height, capture_frame_rate, video_bitrate, fec_numerator, fec_denominator); + break; + } + } + } + + char init_packet[PACKET_SIZE] = "confirmed"; + rc = vpx_net_sendto(&vpx_socket, (tc8 *) &init_packet, PACKET_SIZE , &bytes_sent, address); + Sleep(200); + rc = vpx_net_sendto(&vpx_socket, (tc8 *) &init_packet, PACKET_SIZE , &bytes_sent, address); + Sleep(200); + rc = vpx_net_sendto(&vpx_socket, (tc8 *) &init_packet, PACKET_SIZE , &bytes_sent, address); +#endif + + vpx_codec_ctx_t encoder; + vpx_img_alloc(&raw, IMG_FMT_YV12, display_width, display_height, 1); + + cfg.rc_target_bitrate = video_bitrate; + + vpx_codec_enc_init(&encoder, &vpx_codec_vp8_cx_algo, &cfg, 0); + vpx_codec_control_(&encoder, VP8E_SET_CPUUSED, cpu_used); + vpx_codec_control_(&encoder, VP8E_SET_STATIC_THRESHOLD, static_threshold); + vpx_codec_control_(&encoder, VP8E_SET_ENABLEAUTOALTREF, 0); + + create_packetizer(&x, XOR, fec_numerator, fec_denominator); + //HRE(CoInitialize(NULL)); + + start_capture(); + vpx_net_set_read_timeout(&vpx_socket2, 1); + + for (i = 0; !_kbhit();) + { + + // if there is nothing to send +#ifndef ONEWAY + rc = vpx_net_recvfrom(&vpx_socket2, one_packet, sizeof(one_packet), &bytes_read, &address2); + + if (rc != TC_OK && rc != TC_WOULDBLOCK && rc != TC_TIMEDOUT) + vpxlog_dbg(LOG_PACKET, "error\n"); + + if (bytes_read == -1) + bytes_read = 0; + + if (bytes_read) + { + unsigned char command = one_packet[0]; + unsigned short seq = *((unsigned short *)(1 + one_packet)); + int bytes_sent; + + PACKET *tp = &x.packet[seq&PSM]; + vpxlog_dbg(SKIP, "Command :%c Seq:%d FT:%c RecoverySeq:%d AltSeq:%d \n", + command, seq, (tp->frame_type == NORMAL ? 'N' : 'G'), + gold_recovery_seq, altref_recovery_seq); + + // requested to resend a packet ( ignore if we are about to send a recovery frame) + if (command == 'r' && request_recovery == 0) + { + rc = vpx_net_sendto(&vpx_socket, (tc8 *) &x.packet[seq&PSM], + PACKET_HEADER_SIZE + x.packet[seq&PSM].size, &bytes_sent, address); + vpxlog_dbg(SKIP, "Sent recovery packet %c:%d, %d,%d\n", command, tp->frame_type, seq, tp->timestamp); + } + + int recovery_seq = gold_recovery_seq; + int recovery_type = GOLD; + int other_recovery_seq = altref_recovery_seq; + int other_recovery_type = ALTREF; + + if ((unsigned short)(recovery_seq - altref_recovery_seq > 32768)) + { + recovery_seq = altref_recovery_seq; + recovery_type = ALTREF; + other_recovery_seq = gold_recovery_seq; + other_recovery_type = GOLD; + } + + // if requested to recover but seq is before recovery RESEND + if ((unsigned short)(seq - recovery_seq) > 32768 || command != 'g') + { + rc = vpx_net_sendto(&vpx_socket, (tc8 *) &x.packet[seq&PSM], + PACKET_HEADER_SIZE + x.packet[seq&PSM].size, &bytes_sent, address); + vpxlog_dbg(SKIP, "Sent recovery packet %c:%d, %d,%d\n", command, tp->frame_type, seq, tp->timestamp); + continue; + } + + // requested recovery frame and its a normal frame packet that's lost and seq is after our recovery frame so make a long term ref frame + if (tp->frame_type == NORMAL && (unsigned short)(seq - recovery_seq) > 0 && (unsigned short)(seq - recovery_seq) < 32768) + { + request_recovery = recovery_type; + vpxlog_dbg(SKIP, "Requested recovery frame %c:%c,%d,%d\n", command, (recovery_type == GOLD ? 'G' : 'A'), x.packet[gold_recovery_seq&PSM].frame_type, seq, x.packet[gold_recovery_seq&PSM].timestamp); + continue; + } + + // so the other one is too old request a recovery frame from our older reference buffer. + if ((unsigned short)(seq - other_recovery_seq) > 0 && (unsigned short)(seq - other_recovery_seq) < 32768) + { + request_recovery = other_recovery_type; + vpxlog_dbg(SKIP, "Requested recovery frame %c:%c,%d,%d\n", command, (other_recovery_type == GOLD ? 'G' : 'A'), x.packet[gold_recovery_seq&PSM].frame_type, seq, x.packet[gold_recovery_seq&PSM].timestamp); + continue; + } + + // nothing else we can do ask for a key + request_recovery = KEY; + vpxlog_dbg(SKIP, "Requested key frame %c:%d,%d\n", command, tp->frame_type, seq, tp->timestamp); + + continue; + } + +#endif + send_packet(&x, &vpx_socket, address); + vpx_net_set_read_timeout(&vpx_socket2, 1); + + // check to see if we have a frame in our packet store. + if (get_frame() == 0) + { + // do we have room in our packet store for a frame + if (x.add_ptr - x.send_ptr < 20) + { + int frame_type; + long long time_in_nano_seconds = (long long)(buffer_time * 10000000.000 + .5); + unsigned int rtptime = (unsigned int)((long long)(buffer_time * 1000000.000) & 0xffffffff); + double fps = 10000000.000 / (time_in_nano_seconds - last_time_in_nanoseconds); + + //printf("%14.4g\n",fps); + const vpx_codec_cx_pkt_t *pkt; + vpx_codec_iter_t iter = NULL; + flags = recovery_flags[request_recovery]; + vpx_codec_encode(&encoder, &raw, time_in_nano_seconds, 30000000, flags, VPX_DL_REALTIME); + ctx_exit_on_error(&encoder, "Failed to encode frame"); + + while ((pkt = vpx_codec_get_cx_data(&encoder, &iter))) + { + if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) + { + last_time_in_nanoseconds = time_in_nano_seconds; + + frame_type = request_recovery; + + // a recovery frame was requested move sendptr to current ptr, so that we + // don't spend datarate sending packets that won't be used. + if (request_recovery) + { + x.send_ptr = x.add_ptr ; + request_recovery = 0; + } + + if (frame_type == GOLD || frame_type == KEY) + gold_recovery_seq = x.seq; + + if (frame_type == ALTREF || frame_type == KEY) + altref_recovery_seq = x.seq; + + packetize(&x, rtptime, (unsigned char *) pkt->data.frame.buf, pkt->data.frame.sz, frame_type); + + vpxlog_dbg(FRAME, "Frame %d %d %d %10.4g %d\n", x.packet[x.send_ptr].seq, pkt->data.frame.sz, x.packet[x.send_ptr].timestamp, fps, gold_recovery_seq); +#ifdef WRITEFILE + fwrite(&pkt->data.frame.sz, 4, 1, out_file); + fwrite(pkt->data.frame.buf, pkt->data.frame.sz, 1, out_file); +#endif + i++; + } + } + } + + buffer_has_frame = false; + } + + } + +#ifdef WINDOWS +// graph->Abort(); + CoUninitialize(); +#endif + + vpx_net_close(&vpx_socket2); + vpx_net_close(&vpx_socket); + vpx_net_destroy(); + +#ifdef WRITEFILE + fclose(out_file); +#endif + + vpx_codec_destroy(&encoder); + vpx_img_free(&raw); + return 0; +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..d5bea83 --- /dev/null +++ b/makefile
@@ -0,0 +1,95 @@ +RM := rm -rf + +# All of the sources participating in the build are defined here +CPP_SRCS := \ +grabcompressandsend.cpp \ +receivedecompressandplay.cpp + +C_SRCS := \ +time.c \ +vpx_network.c + +OBJS := \ +time.o \ +vpx_network.o + +CPP_DEPS := \ +./grabcompressandsend.d \ +./receivedecompressandplay.d + +C_DEPS := \ +./time.d \ +./vpx_network.d + +UNAME := $(shell uname) + +ifeq ($(UNAME), Linux) + C_FLAGS := -DLINUX -O0 -g3 -Wall -c -fmessage-length=0 -m64 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" + RLIBS := -lvpx -lpthread -lrt -lSDL + SLIBS := -lvpx -lpthread -lrt + L_FLAGS := -m64 +else +ifeq ($(UNAME), Darwin) + C_FLAGS := -DLINUX -DMACOSX -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" + RLIBS := -lvpx -lpthread -lSDL -lpthread -lSDLmain -framework cocoa + SLIBS := -framework Carbon -framework QuartzCore -framework QuickTime -lvpx -lpthread -framework cocoa -lvidcap + L_FLAGS := -D_THREAD_SAFE +else +ifeq ($(UNAME), CYGWIN_NT-6.1) + C_FLAGS := -DLINUX -DMACOSX -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" + RLIBS := -lvpx -lpthread -lrt -lSDL + SLIBS := -lvpx -lpthread -lrt -lvidcap + L_FLAGS := +else + $(error Unknown System need to fix this make file!) +endif +endif +endif +EXECUTABLES := grabcompressandsend receivedecompressandplay + + +# Each subdirectory must supply rules for building sources it contributes +%.o: %.cpp + @echo 'Building file: $<' + @echo 'Invoking: GCC C++ Compiler' + g++ $(C_FLAGS) -o"$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + +%.o: %.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + gcc $(C_FLAGS) -o"$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + + + +# Add inputs and outputs from these tool invocations to the build variables + +# All Target +all: grabcompressandsend receivedecompressandplay + +# Tool invocations +grabcompressandsend: $(OBJS) $(USER_OBJS) ./grabcompressandsend.o + @echo 'Building target: $@' + @echo 'Invoking: GCC C++ Linker' + @echo g++ $(L_FLAGS) -o "grabcompressandsend" ./grabcompressandsend.o $(OBJS) $(SLIBS) + g++ $(L_FLAGS) -o "grabcompressandsend" ./grabcompressandsend.o $(OBJS) $(SLIBS) + @echo 'Finished building target: $@' + @echo ' ' + +receivedecompressandplay: $(OBJS) $(USER_OBJS) ./receivedecompressandplay.o + @echo 'Building target: $@' + @echo 'Invoking: GCC C++ Linker' + g++ $(L_FLAGS) -o "receivedecompressandplay" ./receivedecompressandplay.o $(OBJS) $(RLIBS) + @echo 'Finished building target: $@' + @echo ' ' + + +# Other Targets +clean: + -$(RM) $(OBJS) $(C_DEPS) $(CPP_DEPS) $(EXECUTABLES) receivedecompressandplay.o grabcompressandsend.o + -@echo ' ' + +
diff --git a/qedit.h b/qedit.h new file mode 100644 index 0000000..fed0ab0 --- /dev/null +++ b/qedit.h
@@ -0,0 +1,86 @@ +#ifndef __qedit_h__ +#define __qedit_h__ + +/////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +/////////////////////////////////////////////////////////////////////////////////// +interface +ISampleGrabberCB +: +public IUnknown +{ + virtual STDMETHODIMP SampleCB(double SampleTime, IMediaSample *pSample) = 0; + virtual STDMETHODIMP BufferCB(double SampleTime, BYTE *pBuffer, long BufferLen) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////////// + + +IID IID_ISampleGrabberCB = { 0x0579154A, 0x2B53, 0x4994, { 0xB0, 0xD0, 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +interface +ISampleGrabber +: +public IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE SetOneShot(BOOL OneShot) = 0; + virtual HRESULT STDMETHODCALLTYPE SetMediaType(const AM_MEDIA_TYPE *pType) = 0; + virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType(AM_MEDIA_TYPE *pType) = 0; + virtual HRESULT STDMETHODCALLTYPE SetBufferSamples(BOOL BufferThem) = 0; + virtual HRESULT STDMETHODCALLTYPE GetCurrentBuffer(long *pBufferSize, long *pBuffer) = 0; + virtual HRESULT STDMETHODCALLTYPE GetCurrentSample(IMediaSample **ppSample) = 0; + virtual HRESULT STDMETHODCALLTYPE SetCallback(ISampleGrabberCB *pCallback, long WhichMethodToCallback) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +IID IID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce, { 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } }; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +CLSID CLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +CLSID CLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +CLSID CLSID_VideoEffects1Category = { 0xcc7bfb42, 0xf175, 0x11d1, { 0xa3, 0x92, 0x0, 0xe0, 0x29, 0x1f, 0x39, 0x59 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +CLSID CLSID_VideoEffects2Category = { 0xcc7bfb43, 0xf175, 0x11d1, { 0xa3, 0x92, 0x0, 0xe0, 0x29, 0x1f, 0x39, 0x59 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +CLSID CLSID_AudioEffects1Category = { 0xcc7bfb44, 0xf175, 0x11d1, { 0xa3, 0x92, 0x0, 0xe0, 0x29, 0x1f, 0x39, 0x59 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +CLSID CLSID_AudioEffects2Category = { 0xcc7bfb45, 0xf175, 0x11d1, { 0xa3, 0x92, 0x0, 0xe0, 0x29, 0x1f, 0x39, 0x59 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +#endif + + + diff --git a/receivedecompressandplay.cpp b/receivedecompressandplay.cpp new file mode 100644 index 0000000..d5314cf --- /dev/null +++ b/receivedecompressandplay.cpp
@@ -0,0 +1,1351 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This example illustrates using VP8 in a packet loss scenario by xmitting + * video over UDP with Forward Error Correction, Packet Resend, and + * some Unique VP8 functionality. + * + */ + +#include "tctypes.h" +#include "vpx_network.h" +#include <stdio.h> +#include <ctype.h> //for tolower +#include <string.h> + + +extern "C" +{ +#include "rtp.h" +#include "vpx/vpx_decoder.h" +#include "vpx/vp8dx.h" +} + +typedef struct +{ + unsigned int seq; + unsigned short arrival; + unsigned int retry; + unsigned short age; + unsigned int received; + unsigned int given_up; +} SKIPS; + +#define SS 256 +#define SSM (SS-1) +#define PS 2048 +#define PSM (PS-1) +#define MAX_NUMERATOR 16 +#define HRE(y) if(FAILED(hr=y)) {vpxlog_dbg(ERRORS,#y##":%x\n",hr);}; + +unsigned short first_seq_ever = 0; +unsigned int lag_In_milli_seconds = 0; +unsigned int first_time_stamp_ever = 0; +unsigned int time_of_first_display = 0; +int given_up = 0; +int givenup_skip = 0; +int display_width = 640; +int display_height = 480; +int capture_frame_rate = 30; +int video_bitrate = 300; +int fec_numerator = 6; +int fec_denominator = 5; +int skip_timeout = 800; +int retry_interval = 50; +unsigned short retry_count = 12; +int drop_simulation = 0; +unsigned short send_port = 1408; +unsigned short recv_port = 1407; +unsigned int quit = 0; +int signalquit = 1; + +unsigned char compressed_video_buffer[400000]; +unsigned char output_video_buffer[1280*1024*3]; +tc8 one_packet[8000]; + +#ifdef WINDOWS + +#include "stdafx.h" +#include <conio.h> +#include <mmsystem.h> +#include <atlbase.h> // ATL CComPtr +#include <ddraw.h> +CComPtr<IDirectDraw7> direct_draw; +DDCAPS caps; +CComPtr<IDirectDrawSurface7> primary_surface, overlay_surface; +CComPtr<IDirectDrawClipper> clipper; +DDOVERLAYFX overlay_fx; +DWORD overlay_flags; +DDSURFACEDESC2 ddsd; +HANDLE thread; +DWORD thread_id; +HWND hwnd; +MSG msg; +WNDCLASS wc; +RECT client_rect; +LRESULT APIENTRY main_wnd_proc(HWND hwnd, UINT msg, UINT parm1, LONG parm2) +{ + INPUT_RECORD ir; + HANDLE console_input; + unsigned int count; + + switch (msg) + { + case WM_DESTROY: + console_input = GetStdHandle(STD_INPUT_HANDLE); + ir.EventType = KEY_EVENT; + ir.Event.KeyEvent.uChar.AsciiChar = 'q'; + WriteConsoleInput(console_input, &ir, 1, (LPDWORD) &count); + PostQuitMessage(0); + break; + case WM_MOVE: + GetWindowRect(hwnd, &client_rect); + DefWindowProc(hwnd, msg, parm1, parm2); + break; + } + + return DefWindowProc(hwnd, msg, parm1, parm2); + +} +char app_name[] = "ReceiveDecompressAndPlay"; +void display_win_main(void *dummy) +{ + wc.style = CS_BYTEALIGNWINDOW; + wc.lpfnWndProc = main_wnd_proc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = 0; + wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = app_name; + RegisterClass(&wc); + + hwnd = CreateWindow(app_name, app_name, WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, + 0, 0, display_width + 9, display_height + 30, NULL, NULL, 0, NULL); + + if (hwnd == NULL) + ExitThread(-1); + + while (GetMessage(&(msg), NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + +} +void setup_surface(void) +{ + HRESULT hr; + thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) display_win_main, (LPVOID) NULL, 0, &thread_id); + + HRE(DirectDrawCreateEx(0, (void **)&direct_draw, IID_IDirectDraw7, 0)); + ZeroMemory(&caps, sizeof(caps)); + caps.dwSize = sizeof(caps); + HRE(direct_draw->GetCaps(&caps, 0)); + HRE(direct_draw->SetCooperativeLevel(0, DDSCL_NORMAL)); + + // Create the primary surface + DDSURFACEDESC2 ddsd; + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + HRE(direct_draw->CreateSurface(&ddsd, &primary_surface, 0)); + + direct_draw->CreateClipper(0, &clipper, NULL); + clipper->SetHWnd(0, hwnd); + primary_surface->SetClipper(clipper); + + // Setup the overlay surface's attributes in the surface descriptor + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDPF_YUV | DDSD_PIXELFORMAT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; + ddsd.dwWidth = display_width; + ddsd.dwHeight = display_height; + ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_YUV; + ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('Y', 'V', '1', '2'); + + // Attempt to create the surface with theses settings + HRE(direct_draw->CreateSurface(&ddsd, &overlay_surface, 0)); + +} +#define INIT_DXSTRUCT(dxs) { ZeroMemory(&dxs, sizeof(dxs)); dxs.dwSize = sizeof(dxs); } + +int show_frame(vpx_image_t *img) +{ + DDSURFACEDESC2 ddsd; + INIT_DXSTRUCT(ddsd); + + HRESULT hr = overlay_surface->Lock(0, &ddsd, DDLOCK_DONOTWAIT | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, 0); + + if (SUCCEEDED(hr)) + { + unsigned char *out = (unsigned char *) ddsd.lpSurface; + unsigned char *in = img->planes[PLANE_Y]; + + for (DWORD i = 0; i < ddsd.dwHeight; i++, out += ddsd.lPitch, in += img->stride[PLANE_Y]) + { + memcpy(out, in , ddsd.dwWidth); + } + + in = img->planes[PLANE_U]; + + for (DWORD i = 0; i < ddsd.dwHeight / 2; i++, out += ddsd.lPitch / 2, in += img->stride[PLANE_U]) + { + memcpy(out, in , ddsd.dwWidth / 2); + } + + in = img->planes[PLANE_V]; + + for (DWORD i = 0; i < ddsd.dwHeight / 2; i++, out += ddsd.lPitch / 2, in += img->stride[PLANE_V]) + { + memcpy(out, in , ddsd.dwWidth / 2); + } + + HRE(overlay_surface->Unlock(0)); + + RECT dest_rect; + dest_rect.left = 0; + dest_rect.top = 0; + dest_rect.right = display_width; + dest_rect.bottom = display_height; + RECT src_rect = dest_rect; + + primary_surface->Blt(&client_rect, overlay_surface, &src_rect, DDBLT_ASYNC, NULL); + } + else + { + switch (hr) + { + case DDERR_INVALIDOBJECT: + printf("DDERR_INVALIDOBJECT\n"); + break; + case DDERR_INVALIDPARAMS: + printf("DDERR_INVALIDPARAMS\n"); + break; + case DDERR_OUTOFMEMORY: + printf("DDERR_OUTOFMEMORY\n"); + break; + case DDERR_SURFACEBUSY: + printf("DDERR_SURFACEBUSY\n"); + break; + case DDERR_SURFACELOST: + printf("DDERR_SURFACELOST\n"); + break; + case DDERR_WASSTILLDRAWING: + printf("DDERR_WASSTILLDRAWING\n"); + break; + default: + printf("other\n"); + break; + }; + } + + return 0; +} +void destroy_surface(void) +{ +} +#else +#define Sleep usleep +extern "C" int _kbhit(void); +#include <SDL/SDL.h> +#include <SDL/SDL_thread.h> +#include <SDL/SDL_audio.h> +#include <SDL/SDL_timer.h> + +#include <strings.h> +#include <iostream> +#include <stdio.h> +using namespace std; + +struct pt_data +{ + SDL_Surface **ptscreen; + SDL_Event *ptsdlevent; + SDL_Rect *drect; + SDL_mutex *affmutex; +} ptdata; + +static Uint32 SDL_VIDEO_Flags = SDL_ANYFORMAT | SDL_DOUBLEBUF | SDL_RESIZABLE; + +static int event_thread(void *data); + +const SDL_VideoInfo *info; +char driver[128]; +const char *videodevice = NULL; +SDL_Surface *pscreen; +SDL_Overlay *overlay; +SDL_Rect drect; +SDL_Event sdlevent; +SDL_Thread *mythread; +SDL_mutex *affmutex; +int status; +unsigned char *p = NULL; +unsigned char d1[500], d2[500]; +int w, h; + +int setup_surface(void) +{ + + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + + if (SDL_VideoDriverName(driver, sizeof(driver))) + { + printf("Video driver: %s\n", driver); + } + + info = SDL_GetVideoInfo(); + + if (videodevice == NULL || *videodevice == 0) + { + videodevice = "/dev/video0"; + } + + pscreen = SDL_SetVideoMode(display_width, display_height, 0, SDL_VIDEO_Flags); + overlay = SDL_CreateYUVOverlay(display_width, display_height, SDL_YV12_OVERLAY, pscreen); + + p = (unsigned char *) overlay->pixels[0]; + drect.x = 0; + drect.y = 0; + drect.w = pscreen->w; + drect.h = pscreen->h; + + SDL_WM_SetCaption("Receive Decompress and Play", NULL); + SDL_LockYUVOverlay(overlay); + SDL_UnlockYUVOverlay(overlay); + + /* initialize thread data */ + ptdata.ptscreen = &pscreen; + ptdata.ptsdlevent = &sdlevent; + ptdata.drect = &drect; + affmutex = SDL_CreateMutex(); + ptdata.affmutex = affmutex; + mythread = SDL_CreateThread(event_thread, (void *) &ptdata); + + return 0; +}; +int show_frame(vpx_image_t *img) +{ + char caption[512]; + sprintf(caption, "Receive Decompress and Play"); + SDL_LockMutex(affmutex); + SDL_WM_SetCaption(caption, NULL); + SDL_LockYUVOverlay(overlay); + int i; + + unsigned char *in = img->planes[PLANE_Y]; + unsigned char *p = (unsigned char *) overlay->pixels[0]; + + for (i = 0; i < display_height; i++, in += img->stride[PLANE_Y], p += display_width) + memcpy(p, in, display_width); + + in = img->planes[PLANE_U]; + + for (i = 0; i < display_height / 2; i++, in += img->stride[PLANE_U], p += display_width / 2) + memcpy(p, in, display_width / 2); + + in = img->planes[PLANE_V]; + + for (i = 0; i < display_height / 2; i++, in += img->stride[PLANE_U], p += display_width / 2) + memcpy(p, in, display_width / 2); + + SDL_UnlockYUVOverlay(overlay); + SDL_DisplayYUVOverlay(overlay, &drect); + SDL_UnlockMutex(affmutex); + return 0; +} + +void destroy_surface(void) +{ + SDL_WaitThread(mythread, &status); + SDL_DestroyMutex(affmutex); + SDL_Quit(); +} + +static int event_thread(void *data) +{ + struct pt_data *gdata = (struct pt_data *) data; + SDL_Surface *pscreen = *gdata->ptscreen; + SDL_Event *sdlevent = gdata->ptsdlevent; + SDL_Rect *drect = gdata->drect; + SDL_mutex *affmutex = gdata->affmutex; + + while (signalquit) + { + SDL_LockMutex(affmutex); + + while (SDL_PollEvent(sdlevent)) //scan the event queue + { + switch (sdlevent->type) + { + case SDL_VIDEORESIZE: + pscreen = SDL_SetVideoMode(sdlevent->resize.w & 0xfffe, + sdlevent->resize.h & 0xfffe, 0, SDL_VIDEO_Flags); + drect->w = sdlevent->resize.w & 0xfffe; + drect->h = sdlevent->resize.h & 0xfffe; + break; + case SDL_KEYUP: + break; + case SDL_KEYDOWN: + + switch (sdlevent->key.keysym.sym) + { + case SDLK_a: + break; + case SDLK_s: + break; + case SDLK_z: + break; + case SDLK_x: + break; + default : + break; + } + + break; + case SDL_QUIT: + printf("\nStop asked\n"); + signalquit = 0; + break; + } + } //end if poll + + SDL_UnlockMutex(affmutex); + SDL_Delay(50); + } //end main loop + + return 0; + +} + +#endif + +typedef struct +{ + unsigned int size; + unsigned int count; + unsigned int add_ptr; + unsigned int max; + unsigned int ssrc; + unsigned short oldest_seq; + SKIPS s[SS]; + unsigned int skip_ptr; + PACKET p[PS]; + unsigned int last_frame_timestamp; + unsigned short last_seq; + +} DEPACKETIZER; +DEPACKETIZER y; + +int create_depacketizer(DEPACKETIZER *x) +{ + unsigned int sn; + x->size = PACKET_SIZE; + x->max = PS; + x->skip_ptr = 0; + x->count = 0; + x->add_ptr = 0; + x->last_frame_timestamp = 0xffffffff; + x->last_seq = 0xffff; + x->ssrc = 411; + + // skip store is initialized to no skips in store + for (sn = 0; sn < SS; sn++) + x->s[sn].received = 1; + + return 0; // SUCCESS +} +int remove_skip(DEPACKETIZER *p, unsigned short seq) +{ + int i; + unsigned int skip_fill = 0; + + // remove packet from skip store if its there it came out of order... + for (i = 0; i < SS; i++) + { + if (seq == p->s[i].seq) + { + p->s[i].received = 1; + p->s[i].given_up = 0; + p->s[i].age = 0; + //p->s[i].seq = 0; + vpxlog_dbg(SKIP, "Unskip %d \n", seq); + skip_fill = 1; + break; + } + } + + return skip_fill; +} +int remove_skip_less(DEPACKETIZER *p, unsigned short seq) +{ + int i; + unsigned int skip_fill = 0; + + // remove packet from skip store if its there it came out of order... + for (i = 0; i < SS; i++) + { + if ((unsigned short)(p->s[i].seq - seq) > 32767 && !p->s[i].received) + { + p->s[i].received = 1; + p->s[i].given_up = 0; + p->s[i].age = 0; + //p->s[i].seq = 0; + vpxlog_dbg(SKIP, "Unskip less than %d : %d \n", seq, p->s[i].seq); + skip_fill = 1; + } + } + + return skip_fill; +} +int add_skip(DEPACKETIZER *p, unsigned short sn) +{ + // maybe we need to check if skip store is completely full? + if (!p->s[p->skip_ptr].given_up && !p->s[p->skip_ptr].received) + { + // if it is what do we do? + sn += 0; + vpxlog_dbg(REBUILD, "Skip Store filled!!!\n"); + } + + // clear data that might mess us up + p->p[sn &PSM].redundant_count = 0; + p->p[sn &PSM].type = DATAPACKET; + p->p[sn &PSM].size = 0; + p->s[p->skip_ptr].arrival = (unsigned short)(get_time() & 0xffff); + p->s[p->skip_ptr].retry = 0; + p->s[p->skip_ptr].seq = sn; + p->s[p->skip_ptr].age = 0; + p->s[p->skip_ptr].received = 0; + p->s[p->skip_ptr].given_up = 0; + p->skip_ptr = ((p->skip_ptr + 1)&SSM); + return 0; +} +void check_recovery(DEPACKETIZER *p, PACKET *x) +{ + if ((x->frame_type == KEY || x->frame_type == GOLD || x->frame_type == ALTREF)) + { + unsigned short seq = x->seq;//p->oldest_seq; + unsigned short lastPossibleSeq = p->oldest_seq;//p->last_seq; + PACKET *tp = &p->p[seq&PSM]; + vpxlog_dbg(REBUILD, "Received keyframe or recovery frame -> %d, %d \n", seq, p->p[x->seq&PSM].timestamp); + + // if we are on a new frame drop everything older than where we are now. + if (x->new_frame) + { + p->oldest_seq = seq; + p->last_frame_timestamp = x->timestamp - 1; + seq--; + remove_skip_less(p, seq); + } + // find first non dropped packet prior to now. + else while (seq != lastPossibleSeq) + { + tp = &p->p[seq&PSM]; + + // new timestamp that isn't empty + if (tp->size != 0 && tp->timestamp != x->timestamp && tp->seq == seq) + { + remove_skip_less(p, seq); + break; + } + + seq--; + } + + given_up = 0; + } +} + +int read_packet(DEPACKETIZER *p, tc8 *data, unsigned int size) +{ + PACKET *x = (PACKET *) data; + unsigned int skip_fill = 0; + + // random drops + if ((rand() & 1023) < drop_simulation) + return 0; + + // wrong ssrc exit + if (p->ssrc != x->ssrc) + return 0; + + // already received the packet (ignore this one) + if (p->p[x->seq &PSM].seq == x->seq && p->p[x->seq&PSM].size) + return 0; + + // on the first received packet record first time ever numbers + if (!first_time_stamp_ever) + { + first_time_stamp_ever = x->timestamp; + first_seq_ever = x->seq; + p->oldest_seq = x->seq; + p->last_seq = p->oldest_seq - 1; + vpxlog_dbg(REBUILD, "Received First TimeStamp ever! -> %d, %d new=%d\n", x->seq, x->timestamp, x->new_frame); + + if (x->new_frame != 1) + { + add_skip(p, x->seq - 1); + p->oldest_seq = x->seq - 1; + vpxlog_dbg(REBUILD, "First packet not start of new frame! -> %d, \n", x->seq - 1); + } + } + + // if we are on the first frame ever and there's an older + if (first_time_stamp_ever == x->timestamp && first_seq_ever > x->seq) + { + first_seq_ever = x->seq; + p->oldest_seq = x->seq; + + if (x->new_frame == 1) + { + first_time_stamp_ever = x->timestamp - 1; + } + else + { + add_skip(p, x->seq - 1); + p->oldest_seq = x->seq - 1; + vpxlog_dbg(REBUILD, "Old seq around! -> %d, \n", x->seq - 1); + } + } + + // toss the packet if its for a frame we've already thrown out or displayed + // maybe roll over is an issue we need to address + if (x->timestamp < p->last_frame_timestamp + 1) + { + vpxlog_dbg(DISCARD, "Tossing old seq :%d \n", x->seq); + + // make sure that if we toss our oldest seq we've seen we update + if (x->seq - p->oldest_seq > 0 && x->seq - p->oldest_seq < 32768) + { + p->oldest_seq = x->seq + 1; + remove_skip_less(p, p->oldest_seq); + } + + return 0; + } + + skip_fill = remove_skip(p, x->seq); + + // this clears the case that we rebuild a packet after we requested a resend + if (!skip_fill && p->last_seq - x->seq > 0 && p->last_seq - x->seq < 32768) + skip_fill = 1; + + // copy to the packet store + x->size = size - PACKET_HEADER_SIZE; + + if (x->size < PACKET_SIZE) + memset(x->data + x->size, 0, PACKET_SIZE - x->size); + + p->p[x->seq &PSM] = *x; + + vpxlog_dbg(LOG_PACKET, "Received Packet %d, %d : new: %d, frame type: %d given_up: %d oldest: %d \n", x->seq, p->p[x->seq&PSM].timestamp, x->new_frame, x->frame_type, given_up, p->oldest_seq); + + // if we get a key frame or recovery frame set this as new frame + check_recovery(p, x); + + // do we have a skip + if (!skip_fill && x->seq != (unsigned short)(p->last_seq + 1) && x->seq != p->last_seq) + { + unsigned short sn; + + // add to skip store + for (sn = p->last_seq + 1; sn != x->seq; sn++) + { + vpxlog_dbg(SKIP, "Skipped Packet %d\n", sn); + add_skip(p, sn); + } + } + + if (!skip_fill) + p->last_seq = x->seq; + + return 0; +} + +int rebuild_packet(DEPACKETIZER *p, unsigned short seq) +{ + unsigned short seqp, seqj; + long long *in[MAX_NUMERATOR]; + long long *out = (long long *) p->p[seq&PSM].data; + unsigned int i, j = 0; + unsigned int redundant_count = 0; + PACKET *pp = &p->p[(seq-1)&PSM]; + PACKET *np = &p->p[(seq+1)&PSM]; + + // if last packet has type count 1 we don't need this one its type! + // don't bother rebuilding + if (pp->redundant_count == 1) + { + p->p[seq &PSM].type = XORPACKET; + p->p[seq &PSM].size = 0; + + if (seq == p->oldest_seq) + p->oldest_seq++; + + return -1; + } + + // if 1 ago is empty, check 2 ago in case we lost redundant packet + if (p->p[(seq-2)&PSM].redundant_count == 1) + pp = &p->p[(seq-2)&PSM]; + + // no point doing this frame before the last one is ready + if (pp->timestamp < p->last_frame_timestamp) + return -1; + + p->p[seq &PSM].type = DATAPACKET; + + // search through subsequent packets for the redundant packet + for (seqp = seq + 1; seqp != (unsigned short)(seq + MAX_NUMERATOR); seqp++) + { + // found redundant packet filled in ? + if (p->p[seqp&PSM].type && p->p[seqp&PSM].size) + { + redundant_count = p->p[seqp&PSM].redundant_count; + + // if initiate call this seq isn't covered. + if (redundant_count < (unsigned short)(seqp - seq)) + { + return -1; + } + + break; + } + } + + // go back through the packets and set up input pointers + for (seqj = seqp; seqj != seqp - 1 - redundant_count; seqj--) + { + // set up pointer to data for each seq in recovery frame + if (seqj != seq) + { + // if its missing or the seq is wrong return a failure. + if (p->p[seqj &PSM].size == 0 || p->p[seqj &PSM].seq != seqj) + { + return -1; + } + + in[j++] = (long long *) p->p[seqj&PSM].data; + } + } + + // nothing was listed as type? + if (!redundant_count) + { + return -1; + } + + // go through a full packet's worth of data. + for (j = 0; j < PACKET_SIZE / sizeof(long long); j++) + { + // start with the most recent packet + *out = *(in[0]); + + // xor all the older packets with out + for (i = 1; i < redundant_count; i++) + { + *out ^= *(in[i]); + in[i]++; + } + + out++; + in[0]++; + } + + // real data filled to the brim with data. + p->p[seq &PSM].seq = seq; + p->p[seq &PSM].type = DATAPACKET; + p->p[seq &PSM].size = PACKET_SIZE; + p->p[seq &PSM].timestamp = pp->timestamp; + p->p[seq &PSM].new_frame = 0; + p->p[seq &PSM].end_frame = 0; + p->p[seq &PSM].frame_type = pp->frame_type; + + // logging what packets we used to rebuild + if (LOG_MASK & REBUILD) + { + unsigned short last = seqj + redundant_count + 2; + seqj++; + vpxlog_dbg(REBUILD, "Rebuilt Lost Sequence :%d, %d from: ", seq, p->p[seq&PSM].timestamp); + + for (; seqj != last; seqj++) + if (seq != seqj) + vpxlog_dbg_no_head(REBUILD, "%d, ", p->p[seqj&PSM].seq); + + vpxlog_dbg_no_head(REBUILD, "\n"); + } + + // if np is type and end_frame this packet ends frame + if (np->end_frame && np->type) + p->p[seq &PSM].end_frame = 1; + + // last packet ends frame + if (pp->end_frame) + { + // if next packet is a new frame we have to fabricate a frame.. + if (np->new_frame) + { + p->p[seq &PSM].timestamp = (pp->timestamp + np->timestamp) / 2; + p->p[seq &PSM].new_frame = 1; + p->p[seq &PSM].end_frame = 1; + } + else + { + // this must be the frame start + p->p[seq &PSM].frame_type = np->frame_type; + p->p[seq &PSM].timestamp = np->timestamp; + p->p[seq &PSM].new_frame = 1; + } + } + + check_recovery(p, &p->p[seq&PSM]); + return 0; +} + +int frame_ready(DEPACKETIZER *p) +{ + // check if we have a whole frame. + unsigned short seq = p->oldest_seq; // f->first_seq; + unsigned short last_possible_seq = p->last_seq; + PACKET *tp = &p->p[seq&PSM]; + + unsigned int timestamp = p->p[seq&PSM].timestamp; + + if (timestamp < p->last_frame_timestamp + 1) + { + vpxlog_dbg(FRAME, "Trying to play an old frame:%d, timestamp :%d , last Time :%d \n", seq, timestamp, p->last_frame_timestamp); + return 0; + } + + // seems like this should be unnecessary??? + while (timestamp && p->p[seq &PSM].timestamp == timestamp && !p->p[seq&PSM].new_frame) + seq--; + + p->oldest_seq = seq; + remove_skip_less(p, p->oldest_seq); + + // first seq not a new frame. Frames not ready. + if (!p->p[seq&PSM].new_frame) + { + if (p->p[(seq-1)&PSM].type == XORPACKET) + p->p[seq &PSM].new_frame = 1; + else + return 0; + } + + // loop through all frames and see if every packet between start and + // end is present or we are missing type frames. + while (seq != last_possible_seq) + { + tp = &p->p[seq&PSM]; + + // timestamp needs to differ and the packet has to have data + if (tp->timestamp != timestamp || tp->size == 0) + { + // here we have a whole frame but end frame marker not set properly + if (tp->new_frame && tp->size > 0) + { + p->p[(seq-1)&PSM].end_frame = 1; + return 1; + } + + // if missing packet is not type frame is not ready. + if (p->p[(seq-1)&PSM].redundant_count != 1) + return 0; + + // make sure frame is marked type + tp->type = XORPACKET; + } + else if (tp->end_frame) + return 1; + + seq++; + } + + return 0; +} +int get_frame(DEPACKETIZER *p, unsigned char *data, int size, unsigned int *outsize, unsigned int *timestamp) +{ + *outsize = 0; + + // check if we have a whole frame. + if (frame_ready(p)) + { + unsigned short seq = p->oldest_seq; + unsigned short last_possible_seq = p->last_seq; + *timestamp = p->p[seq&PSM].timestamp; + + // build a frame from the packets we have. + while (seq != last_possible_seq) + { + PACKET *tp = &p->p[seq&PSM]; + + // timestamp needs to match and size must be > 0 + if (tp->timestamp == *timestamp && tp->size > 0 && tp->type == DATAPACKET) + { + memcpy(data, tp->data, tp->size); + data += tp->size; + *outsize += tp->size; + tp->size = 0; + + if (tp->end_frame) + break; + } + + // its a skip clear from skip remove it + if (tp->size == 0) + { + remove_skip(p, seq); + } + + seq++; + } + + // if we have a xorpacket frame at the end of our frame throw it out + if (p->p[(seq+1)&PSM].timestamp == *timestamp && p->p[(seq+1)&PSM].type == XORPACKET) + { + seq++; + } + + p->last_frame_timestamp = *timestamp; + p->oldest_seq = seq + 1; + + + return 1; + } + + return 0; +} + +int age_skip_store(DEPACKETIZER *p, struct vpxsocket *vpx_sock, union vpx_sockaddr_x *address) +{ + unsigned int request_count = 0; + unsigned int i; + unsigned short now = (unsigned short)(get_time() & 0xffff); + + if (given_up) + { + // we've given up on a frame do nothing else until we get a recovery frame. + unsigned short time_to_retry = 0; + unsigned short seq = p->s[givenup_skip].seq; + + if (p->s[givenup_skip].arrival <= now) + p->s[givenup_skip].age = now - p->s[givenup_skip].arrival; + else + p->s[givenup_skip].age = (unsigned short)((unsigned int)(0xffff + now) - p->s[givenup_skip].arrival); + + time_to_retry = (p->s[givenup_skip].age > (p->s[givenup_skip].retry * retry_interval)); + + if (time_to_retry && ((rand() & 1023) >= drop_simulation)) + { + // Tell the sender we want to give up + int bytes_sent; + tc8 buffer[40]; + buffer[0] = 'g'; + buffer[1] = seq & 0x00ff; + buffer[2] = (seq & 0xff00) >> 8; + vpx_net_sendto(vpx_sock, buffer, 3, &bytes_sent, *address); + vpxlog_dbg(DISCARD, "Give up forever on sequence %d now %d :age :%d retry:%d \n", seq, now, p->s[givenup_skip].age, p->s[givenup_skip].retry); + p->s[givenup_skip].retry ++; + } + + return 0; + } + + for (i = 0; i < SS; i++) + { + if (!p->s[i].received && !p->s[i].given_up) + { + request_count ++; + } + } + + // go through the skip store + for (i = 0; i < SS; i++) + { + // if this skip is still in play + if (!p->s[i].received && !p->s[i].given_up) + { + unsigned short seq = p->s[i].seq; + unsigned short time_to_retry = 0; + unsigned int is_redundant = (p->p[(p->s[i].seq - 1)&PSM].redundant_count == 1); + + // calculate the age of the skip including wrap around + if (p->s[i].arrival <= now) + p->s[i].age = now - p->s[i].arrival; + else + p->s[i].age = (unsigned short)((unsigned int)(0xffff + now) - p->s[i].arrival); + + time_to_retry = (p->s[i].age > (p->s[i].retry * retry_interval)); + + // if its redundant don't bother rebuilding requesting it again. + if (is_redundant) + { + p->s[i].given_up = 1; + p->p[p->s[i].seq &PSM].size = 0; + + vpxlog_dbg(LOG_PACKET, "Lost redundant packet %d, ignoring \n", seq); + + } + // try and rebuild from recovery packets + else if (rebuild_packet(p, seq) == 0) + { + p->s[i].received = 1; + p->s[i].age = 0; + } + // time to give up we wasted enough time + else if (time_to_retry && (p->s[i].age > skip_timeout || request_count > retry_count)) + { + given_up = 1; + givenup_skip = i; + vpxlog_dbg(LOG_PACKET, "Giving up: %d age:%d request_count:%d\n", seq, p->s[i].age, request_count); + break; + } + // request a resend + else if (time_to_retry && ((rand() & 1023) >= drop_simulation)) + { + int bytes_sent; + tc8 buffer[40]; + buffer[0] = 'r'; + buffer[1] = seq & 0x00ff; + buffer[2] = (seq & 0xff00) >> 8; + vpx_net_sendto(vpx_sock, buffer, 3, &bytes_sent, *address); + vpxlog_dbg(DISCARD, "Lost %d, skip: %d, Requesting Resend\n", seq, i); + p->s[i].retry++; + } + } + + // if we're giving up on this one and its the oldest of the bunch increase the oldest seq + if (p->oldest_seq == p->s[i].seq && p->s[i].given_up) + { + p->oldest_seq++; + } + } + + return 0; +} +//#define DEBUG_FILES 1 +#ifdef DEBUG_FILES +void debug_frame(vpx_image_t *img) +{ + unsigned char *in = img->planes[PLANE_Y]; + + for (unsigned int i = 0; i < display_height; i++, in += img->stride[PLANE_Y]) + { + fwrite(in , display_width, 1, outFile); + } + + in = img->planes[PLANE_U]; + + for (unsigned int i = 0; i < display_height / 2; i++, in += img->stride[PLANE_U]) + { + fwrite(in , display_width / 2, 1, outFile); + } + + in = img->planes[PLANE_V]; + + for (unsigned int i = 0; i < display_height / 2; i++, in += img->stride[PLANE_V]) + { + fwrite(in , display_width / 2, 1, outFile); + } + +} +#endif + +int main(int argc, char *argv[]) +{ + printf("ReceiveDecompressAndPlay (-? for help) \n"); + + while (--argc > 0) + { + if (argv[argc][0] == '-') + { + switch (argv[argc][1]) + { + case 'w': + case 'W': + display_width = atoi(argv[argc--+1]); + break; + case 'h': + case 'H': + display_height = atoi(argv[argc--+1]); + break; + case 'f': + case 'F': + capture_frame_rate = atoi(argv[argc--+1]); + break; + case 'b': + case 'B': + video_bitrate = atoi(argv[argc--+1]); + break; + case 'n': + case 'N': + fec_numerator = atoi(argv[argc--+1]); + break; + case 'd': + case 'D': + fec_denominator = atoi(argv[argc--+1]); + break; + case 't': + case 'T': + skip_timeout = atoi(argv[argc--+1]); + break; + case 'i': + case 'I': + retry_interval = atoi(argv[argc--+1]); + break; + case 'c': + case 'C': + retry_count = atoi(argv[argc--+1]); + break; + case 'l': + case 'L': + drop_simulation = atoi(argv[argc--+1]); + break; + case 's': + case 'S': + send_port = atoi(argv[argc--+1]); + break; + case 'r': + case 'R': + recv_port = atoi(argv[argc--+1]); + break; + default: + printf( + "ReceiveDecompressAndPlay: \n" + "========================: \n" + "Receives, decompresses and plays video received from the GrabCompressAndSend sample.\n\n" + "-w [640] request capture width \n" + "-h [480] request capture height \n" + "-f [30] request capture frame rate\n" + "-b [300] video_bitrate = ato\n" + "-n [6] fec_numerator ( redundancy numerator)\n" + "-d [5] fec_denominator ( redundancy denominator) \n" + " 6/5 means 1 xor packet for every 5 packets, \n" + " 4/1 means 3 duplicate packets for every packet\n" + "-t [800] milliseconds before giving up and requesting recovery \n" + "-i [50] time in milliseconds between attempts at a packet resend\n" + "-c [12] number of lost packets before requesting recovery \n" + "-l [0] packets to lose out of every 1000 \n" + "-s [1408] port to send requests to\n" + "-r [1407] port to receive requests on. \n" + "\n"); + exit(0); + break; + } + } + } + + vpxlog_dbg(FRAME, "%dx%d %dfps, %dkbps, %d/%dFEC,%d skip, %d retry interval, %d count, %d drop simulation \n", + display_width, display_height, capture_frame_rate, video_bitrate, fec_numerator, fec_denominator , + skip_timeout, retry_interval, retry_count, drop_simulation); + + + struct vpxsocket vpx_sock, vpx_sock2; + union vpx_sockaddr_x address, address2; + + TCRV rc; + tc32 bytes_read; + +#ifdef DEBUG_FILES + FILE *f = fopen("out2.rtp", "wb"); + char fn[512]; + sprintf(fn, "decoded_%dx%d", display_width, display_height); + FILE *out_file = fopen(fn, "wb"); + FILE *vpx_file = fopen("decode.vpx", "wb"); +#endif + + int responded = 0; + + vpx_dec_ctx_t decoder; + uint8_t *buf = NULL; + vp8_postproc_cfg_t ppcfg; + vpx_codec_dec_cfg_t cfg = {0}; +// vpx_dec_init(&decoder, &vpx_codec_vp8_algo); + vpx_codec_dec_init(&decoder, &vpx_codec_vp8_dx_algo, &cfg, 0); + + buf = (uint8_t *) malloc(display_width * display_height * 3 / 2); + + /* Config post processing settings for decoder */ + ppcfg.post_proc_flag = VP8_DEMACROBLOCK | VP8_DEBLOCK | VP8_ADDNOISE; + ppcfg.deblocking_level = 5 ; + ppcfg.noise_level = 1 ; + vpx_codec_control(&decoder, VP8_SET_POSTPROC, &ppcfg); + + create_depacketizer(&y); + + vpx_net_init(); + + if (TC_OK != vpx_net_open(&vpx_sock, vpx_IPv4, vpx_UDP)) + return -1; + + vpx_net_set_read_timeout(&vpx_sock, 20); + vpx_net_bind(&vpx_sock, 0, recv_port); + + if (TC_OK != vpx_net_open(&vpx_sock2, vpx_IPv4, vpx_UDP)) + return -1; + + int bytes_sent; + + while (!_kbhit()) + { + char initPacket[PACKET_SIZE]; + sprintf(initPacket, "configuration %d %d %d %d %d %d ", display_width, display_height, capture_frame_rate, video_bitrate, fec_numerator, fec_denominator); + rc = vpx_net_recvfrom(&vpx_sock, one_packet, sizeof(one_packet), &bytes_read, &address); + + if (rc != TC_OK && rc != TC_WOULDBLOCK && rc != TC_TIMEDOUT) + vpxlog_dbg(DISCARD, "error\n"); + + if (bytes_read == -1) + bytes_read = 0; + + if (bytes_read) + { + if (!responded) + { + char add[400]; + sprintf(add, "%d.%d.%d.%d", + ((unsigned char *)&address.sa_in.sin_addr)[0], + ((unsigned char *)&address.sa_in.sin_addr)[1], + ((unsigned char *)&address.sa_in.sin_addr)[2], + ((unsigned char *)&address.sa_in.sin_addr)[3]); + + vpxlog_dbg(LOG_PACKET, "Address of Sender : %s \n", add); + vpx_net_get_addr_info(add, send_port, vpx_IPv4, vpx_UDP, &address2); + responded = 1; + } + + if (strncmp(one_packet, "initiate call", PACKET_SIZE) == 0) + { + rc = vpx_net_sendto(&vpx_sock2, (tc8 *) &initPacket, PACKET_SIZE , &bytes_sent, address2); + } + + if (strncmp(one_packet, "confirmed", PACKET_SIZE) == 0) + { + rc = vpx_net_sendto(&vpx_sock2, (tc8 *) &initPacket, PACKET_SIZE , &bytes_sent, address2); + break; + } + } + + Sleep(200); + } + + setup_surface(); + + /* Message loop for display window's thread */ + while (!_kbhit() && signalquit) + { + rc = vpx_net_recvfrom(&vpx_sock, one_packet, sizeof(one_packet), &bytes_read, &address); + + if (rc != TC_OK && rc != TC_WOULDBLOCK && rc != TC_TIMEDOUT) + vpxlog_dbg(DISCARD, "error %d\n", rc); + + if (bytes_read == -1) + { +// vpxlog_dbg("-1 bytes_read \n"); + bytes_read = 0; + } + + if (bytes_read) + { + unsigned int timestamp; + unsigned int size; + read_packet(&y, one_packet, bytes_read); + + while (get_frame(&y, compressed_video_buffer, sizeof(compressed_video_buffer), &size, ×tamp)) + { + lag_In_milli_seconds = (unsigned int)((timestamp - first_time_stamp_ever) / 1000.0 - (get_time() - time_of_first_display)); + vpxlog_dbg(FRAME, "Received frame %d, Lag: %d \n", timestamp, lag_In_milli_seconds); + +#ifdef DEBUG_FILES + fwrite(&size, 4, 1, vpx_file); + fwrite(compressed_video_buffer, size, 1, vpx_file); +#endif + + if (!time_of_first_display) + { +#ifdef WINDOWS + ShowWindow(hwnd, SW_SHOWNOACTIVATE); + UpdateWindow(hwnd); +#endif + time_of_first_display = get_time(); + } + + vpx_dec_iter_t iter = NULL; + vpx_image_t *img; + + if (vpx_codec_decode(&decoder, compressed_video_buffer, sizeof(compressed_video_buffer), 0, 0)) + { + vpxlog_dbg(FRAME, "Failed to decode frame: %s\n", vpx_codec_error(&decoder)); + return -1; + } + + img = vpx_codec_get_frame(&decoder, &iter); + show_frame(img); +#ifdef DEBUG_FILES + debug_frame(img); +#endif + + }; + + if (!responded) + { + char add[400]; + sprintf(add, "%d.%d.%d.%d", + ((char *)&address.sa_in.sin_addr)[0], + ((char *)&address.sa_in.sin_addr)[1], + ((char *)&address.sa_in.sin_addr)[2], + ((char *)&address.sa_in.sin_addr)[3]); + + vpxlog_dbg(LOG_PACKET, "Address of Sender : %s \n", add); + vpx_net_get_addr_info(add, send_port, vpx_IPv4, vpx_UDP, &address2); + responded = 1; + } + +#ifdef DEBUG_FILES + fwrite(one_packet, bytes_read, 1, f); +#endif + + } + else + age_skip_store(&y, &vpx_sock2, &address2); + + } + + signalquit = 0; + +#ifdef DEBUG_FILES + fclose(f); + fclose(out_file); + fclose(vpx_file); +#endif + + if (vpx_codec_destroy(&decoder)) + { + vpxlog_dbg(DISCARD, "Failed to destroy decoder: %s\n", vpx_codec_error(&decoder)); + return -1; + } + + free(buf); + + vpx_net_close(&vpx_sock); + vpx_net_destroy(); + destroy_surface(); + return 0; +} diff --git a/rtp.h b/rtp.h new file mode 100644 index 0000000..1097f5d --- /dev/null +++ b/rtp.h
@@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#define LARGESTFRAMESIZE 1000000 +#define PACKET_SIZE 1400 + +enum +{ + DATAPACKET = 0, + XORPACKET = 1, +}; +enum +{ + NORMAL = 0, + KEY = 1, + GOLD = 2, + ALTREF = 3 +}; +enum +{ + LOG_PACKET = 1, + SKIP = 2, + REBUILD = 4, + DISCARD = 8, + FRAME = 16, + ERRORS = 32, +}; + + +#define LOG_MASK ( ERRORS| SKIP|REBUILD|DISCARD ) // ( ERROR|LOG_PACKET|FRAME|SKIP|REBUILD|DISCARD ) // + +typedef struct +{ + int version : 2; + int pad : 1; + int extension : 1; + int csrccount : 4; + int marker : 1; + int payloadtype : 7; + unsigned short seq; + unsigned int timestamp; + + unsigned int ssrc ; + unsigned int csrc ; // repeated up to 15 times + + unsigned int type : 1; + unsigned int redundant_count : 3; + unsigned int new_frame: 1; + unsigned int end_frame: 1; + unsigned int frame_type: 2; + + unsigned char data[PACKET_SIZE]; + + // this value doesn't actually get written or read + unsigned int size; + +} PACKET; + +#define PACKET_HEADER_SIZE offsetof(PACKET,data) + +unsigned int get_time(void); +void vpxlog_dbg_no_head(int level, const tc8 *format, ...); +void vpxlog_dbg(int level, const tc8 *format, ...); diff --git a/stdafx.cpp b/stdafx.cpp new file mode 100644 index 0000000..6e23ca0 --- /dev/null +++ b/stdafx.cpp
@@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// UDPSAMPLE.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file
diff --git a/stdafx.h b/stdafx.h new file mode 100644 index 0000000..07be10a --- /dev/null +++ b/stdafx.h
@@ -0,0 +1,17 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#include <stdio.h> +#include <tchar.h> + + + +// TODO: reference additional headers your program requires here
diff --git a/tctypes.h b/tctypes.h new file mode 100644 index 0000000..a04cadc --- /dev/null +++ b/tctypes.h
@@ -0,0 +1,87 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef __TCTYPES_H__ +#define __TCTYPES_H__ + +/* basic types */ + +typedef char tc8; +typedef unsigned char tcu8; +typedef short tc16; +typedef unsigned short tcu16; +typedef int tc32; +typedef unsigned int tcu32; + +#if defined(LINUX) +# define TC64 "lld" +typedef long long tc64; +#elif defined(WIN32) || defined(_WIN32_WCE) +# define TC64 "I64d" +typedef __int64 tc64; +#elif defined(VXWORKS) || defined(NDS_NITRO) +# define TC64 "lld" +typedef long long tc64; +#elif defined(__uClinux__) && defined(CHIP_DM642) +# include <lddk.h> +# define TC64 "lld" +typedef long tc64; +#elif defined(__SYMBIAN32__)&&!defined(__WINS__) +#include "e32std.h" +#define TC64 "lld" +typedef TInt64 tc64; +#elif defined(__SYMBIAN32__)&&defined(__WINS__) +#define TC64 "lld" +typedef long long tc64; +#else +#define TC64 "lld" +typedef long long tc64; +#endif + +#ifndef __SYMBIAN32__ +/*not carrying over the I64INT as it actually has the potential to change the + calculation as it effectively casts to int*/ +# define I64REAL(x) ((double)(x)) +#endif + +#define tcFalse 0 +#define tcTrue 1 + +/* END - basic types */ + +/* TrueCast return codes; common to all libs */ + +enum eTCRV +{ + TC_FILE_NOT_FOUND = -404, + + TC_BUFFER_UNDERRUN = -203, + TC_BUFFER_EMPTY = -202, + TC_BUFFER_FULL = -201, + + TC_MSG_TOO_LARGE = -102, + TC_TIMEDOUT = -101, + TC_WOULDBLOCK = -100, + + TC_INVALID_VERSION = -8, + TC_INPROGRESS = -7, + TC_INVALID_STATE_CHANGE = -6, + TC_NO_PLUGIN = -5, + TC_RESOURCE_LOCKED = -4, + TC_NO_MEM = -3, + TC_INVALID_PARAMS = -2, + TC_ERROR = -1, + TC_OK = 0, + + TC_DONE = 1 +}; +typedef enum eTCRV TCRV; + +#endif //__TCTYPES_H__ diff --git a/time.c b/time.c new file mode 100644 index 0000000..859ebe3 --- /dev/null +++ b/time.c
@@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +#ifdef WINDOWS +#include <windows.h> +#include <mmsystem.h> +unsigned int get_time(void) +{ + LARGE_INTEGER pf; + long long now; + QueryPerformanceCounter((LARGE_INTEGER *) &now); + QueryPerformanceFrequency(&pf); + return (unsigned int)(now * 1000 / pf.LowPart); +} +#else +#include <time.h> +#include <sys/time.h> + +unsigned int get_time(void) +{ + struct timespec ts; + unsigned long long tv; + +#if _POSIX_TIMERS > 0 + clock_gettime(CLOCK_REALTIME, &ts); +#else +struct timeval tv2; +gettimeofday(&tv2, NULL); +ts.tv_sec = tv2.tv_sec; +ts.tv_nsec = tv2.tv_usec * 1000; +#endif + tv = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + return tv & 0xffffffff; +} +int _kbhit(void) +{ + struct timeval tv; + fd_set read_fd; + + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&read_fd); + FD_SET(0, &read_fd); + + if (select(1, &read_fd, NULL, NULL, &tv) == -1) + return 0; + + if (FD_ISSET(0, &read_fd)) + return 1; + + return 0; +} + + +#endif + +#include "tctypes.h" +#include "rtp.h" +#include <stdarg.h> +#include <stdio.h> + +unsigned int start_time = 0; + +void vpxlog_dbg_no_head(int level, const tc8 *format, ...) +{ + va_list list; + + if (!(level & LOG_MASK)) + return; + + va_start(list, format); + + vprintf(format, list); + va_end(list); +} + +void vpxlog_dbg(int level, const tc8 *format, ...) +{ + va_list list; + + if (!(level & LOG_MASK)) + return; + + if (start_time == 0) + start_time = get_time(); + + printf("%8d ", get_time() - start_time); + va_start(list, format); + + vprintf(format, list); + va_end(list); +} + diff --git a/uvc_compat.h b/uvc_compat.h new file mode 100644 index 0000000..44a999f --- /dev/null +++ b/uvc_compat.h
@@ -0,0 +1,142 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef _UVC_COMPAT_H +#define _UVC_COMPAT_H + +#include <linux/version.h> + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) +/* + * Extended control API + */ +struct v4l2_ext_control +{ + __u32 id; + __u32 reserved2[2]; + union + { + __s32 value; + __s64 value64; + void *reserved; + }; +} __attribute__((packed)); + +struct v4l2_ext_controls +{ + __u32 ctrl_class; + __u32 count; + __u32 error_idx; + __u32 reserved[2]; + struct v4l2_ext_control *controls; +}; + +/* Values for ctrl_class field */ +#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */ +#define V4L2_CTRL_CLASS_MPEG 0x00990000 /* MPEG-compression controls */ + +#define V4L2_CTRL_ID_MASK (0x0fffffff) +#define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) +#define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000) + +/* User-class control IDs defined by V4L2 */ +#undef V4L2_CID_BASE +#define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900) +#define V4L2_CID_USER_BASE V4L2_CID_BASE +#define V4L2_CID_USER_CLASS (V4L2_CTRL_CLASS_USER | 1) + +#define VIDIOC_G_EXT_CTRLS _IOWR ('V', 71, struct v4l2_ext_controls) +#define VIDIOC_S_EXT_CTRLS _IOWR ('V', 72, struct v4l2_ext_controls) +#define VIDIOC_TRY_EXT_CTRLS _IOWR ('V', 73, struct v4l2_ext_controls) + +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +/* + * Frame size and frame rate enumeration + * + * Included in Linux 2.6.19 + */ +enum v4l2_frmsizetypes +{ + V4L2_FRMSIZE_TYPE_DISCRETE = 1, + V4L2_FRMSIZE_TYPE_CONTINUOUS = 2, + V4L2_FRMSIZE_TYPE_STEPWISE = 3, +}; + +struct v4l2_frmsize_discrete +{ + __u32 width; /* Frame width [pixel] */ + __u32 height; /* Frame height [pixel] */ +}; + +struct v4l2_frmsize_stepwise +{ + __u32 min_width; /* Minimum frame width [pixel] */ + __u32 max_width; /* Maximum frame width [pixel] */ + __u32 step_width; /* Frame width step size [pixel] */ + __u32 min_height; /* Minimum frame height [pixel] */ + __u32 max_height; /* Maximum frame height [pixel] */ + __u32 step_height; /* Frame height step size [pixel] */ +}; + +struct v4l2_frmsizeenum +{ + __u32 index; /* Frame size number */ + __u32 pixel_format; /* Pixel format */ + __u32 type; /* Frame size type the device supports. */ + + union /* Frame size */ + { + struct v4l2_frmsize_discrete discrete; + struct v4l2_frmsize_stepwise stepwise; + }; + + __u32 reserved[2]; /* Reserved space for future use */ +}; + +enum v4l2_frmivaltypes +{ + V4L2_FRMIVAL_TYPE_DISCRETE = 1, + V4L2_FRMIVAL_TYPE_CONTINUOUS = 2, + V4L2_FRMIVAL_TYPE_STEPWISE = 3, +}; + +struct v4l2_frmival_stepwise +{ + struct v4l2_fract min; /* Minimum frame interval [s] */ + struct v4l2_fract max; /* Maximum frame interval [s] */ + struct v4l2_fract step; /* Frame interval step size [s] */ +}; + +struct v4l2_frmivalenum +{ + __u32 index; /* Frame format index */ + __u32 pixel_format; /* Pixel format */ + __u32 width; /* Frame width */ + __u32 height; /* Frame height */ + __u32 type; /* Frame interval type the device supports. */ + + union /* Frame interval */ + { + struct v4l2_fract discrete; + struct v4l2_frmival_stepwise stepwise; + }; + + __u32 reserved[2]; /* Reserved space for future use */ +}; + +#define VIDIOC_ENUM_FRAMESIZES _IOWR ('V', 74, struct v4l2_frmsizeenum) +#define VIDIOC_ENUM_FRAMEINTERVALS _IOWR ('V', 75, struct v4l2_frmivalenum) +#endif + + +#endif /* _UVC_COMPAT_H */ + diff --git a/uvcvideo.h b/uvcvideo.h new file mode 100644 index 0000000..05df194 --- /dev/null +++ b/uvcvideo.h
@@ -0,0 +1,718 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef _USB_VIDEO_H_ +#define _USB_VIDEO_H_ + +#include <linux/kernel.h> +#include <linux/videodev.h> + +/* Compatibility */ +#include "uvc_compat.h" + +/* + * Private V4L2 control identifiers. + */ + +#if not defined(V4L2_CID_BACKLIGHT_COMPENSATION) +#define V4L2_CID_BACKLIGHT_COMPENSATION (V4L2_CID_PRIVATE_BASE+0) +#define V4L2_CID_POWER_LINE_FREQUENCY (V4L2_CID_PRIVATE_BASE+1) +#define V4L2_CID_SHARPNESS (V4L2_CID_PRIVATE_BASE+2) +#define V4L2_CID_HUE_AUTO (V4L2_CID_PRIVATE_BASE+3) + +#define V4L2_CID_FOCUS_AUTO (V4L2_CID_PRIVATE_BASE+4) +#define V4L2_CID_FOCUS_ABSOLUTE (V4L2_CID_PRIVATE_BASE+5) +#define V4L2_CID_FOCUS_RELATIVE (V4L2_CID_PRIVATE_BASE+6) + +#define V4L2_CID_PAN_RELATIVE (V4L2_CID_PRIVATE_BASE+7) +#define V4L2_CID_TILT_RELATIVE (V4L2_CID_PRIVATE_BASE+8) +#define V4L2_CID_PANTILT_RESET (V4L2_CID_PRIVATE_BASE+9) + +#define V4L2_CID_EXPOSURE_AUTO (V4L2_CID_PRIVATE_BASE+10) +#define V4L2_CID_EXPOSURE_ABSOLUTE (V4L2_CID_PRIVATE_BASE+11) + +#define V4L2_CID_WHITE_BALANCE_TEMPERATURE_AUTO (V4L2_CID_PRIVATE_BASE+12) +#define V4L2_CID_WHITE_BALANCE_TEMPERATURE (V4L2_CID_PRIVATE_BASE+13) + +#endif + +#define V4L2_CID_PRIVATE_LAST V4L2_CID_WHITE_BALANCE_TEMPERATURE + +#ifdef __KERNEL__ + +/* -------------------------------------------------------------------------- + * UVC constants + */ + +#define SC_UNDEFINED 0x00 +#define SC_VIDEOCONTROL 0x01 +#define SC_VIDEOSTREAMING 0x02 +#define SC_VIDEO_INTERFACE_COLLECTION 0x03 + +#define PC_PROTOCOL_UNDEFINED 0x00 + +#define CS_UNDEFINED 0x20 +#define CS_DEVICE 0x21 +#define CS_CONFIGURATION 0x22 +#define CS_STRING 0x23 +#define CS_INTERFACE 0x24 +#define CS_ENDPOINT 0x25 + +/* VideoControl class specific interface descriptor */ +#define VC_DESCRIPTOR_UNDEFINED 0x00 +#define VC_HEADER 0x01 +#define VC_INPUT_TERMINAL 0x02 +#define VC_OUTPUT_TERMINAL 0x03 +#define VC_SELECTOR_UNIT 0x04 +#define VC_PROCESSING_UNIT 0x05 +#define VC_EXTENSION_UNIT 0x06 + +/* VideoStreaming class specific interface descriptor */ +#define VS_UNDEFINED 0x00 +#define VS_INPUT_HEADER 0x01 +#define VS_OUTPUT_HEADER 0x02 +#define VS_STILL_IMAGE_FRAME 0x03 +#define VS_FORMAT_UNCOMPRESSED 0x04 +#define VS_FRAME_UNCOMPRESSED 0x05 +#define VS_FORMAT_MJPEG 0x06 +#define VS_FRAME_MJPEG 0x07 +#define VS_FORMAT_MPEG2TS 0x0a +#define VS_FORMAT_DV 0x0c +#define VS_COLORFORMAT 0x0d +#define VS_FORMAT_FRAME_BASED 0x10 +#define VS_FRAME_FRAME_BASED 0x11 +#define VS_FORMAT_STREAM_BASED 0x12 + +/* Endpoint type */ +#define EP_UNDEFINED 0x00 +#define EP_GENERAL 0x01 +#define EP_ENDPOINT 0x02 +#define EP_INTERRUPT 0x03 + +/* Request codes */ +#define RC_UNDEFINED 0x00 +#define SET_CUR 0x01 +#define GET_CUR 0x81 +#define GET_MIN 0x82 +#define GET_MAX 0x83 +#define GET_RES 0x84 +#define GET_LEN 0x85 +#define GET_INFO 0x86 +#define GET_DEF 0x87 + +/* VideoControl interface controls */ +#define VC_CONTROL_UNDEFINED 0x00 +#define VC_VIDEO_POWER_MODE_CONTROL 0x01 +#define VC_REQUEST_ERROR_CODE_CONTROL 0x02 + +/* Terminal controls */ +#define TE_CONTROL_UNDEFINED 0x00 + +/* Selector Unit controls */ +#define SU_CONTROL_UNDEFINED 0x00 +#define SU_INPUT_SELECT_CONTROL 0x01 + +/* Camera Terminal controls */ +#define CT_CONTROL_UNDEFINED 0x00 +#define CT_SCANNING_MODE_CONTROL 0x01 +#define CT_AE_MODE_CONTROL 0x02 +#define CT_AE_PRIORITY_CONTROL 0x03 +#define CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04 +#define CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05 +#define CT_FOCUS_ABSOLUTE_CONTROL 0x06 +#define CT_FOCUS_RELATIVE_CONTROL 0x07 +#define CT_FOCUS_AUTO_CONTROL 0x08 +#define CT_IRIS_ABSOLUTE_CONTROL 0x09 +#define CT_IRIS_RELATIVE_CONTROL 0x0a +#define CT_ZOOM_ABSOLUTE_CONTROL 0x0b +#define CT_ZOOM_RELATIVE_CONTROL 0x0c +#define CT_PANTILT_ABSOLUTE_CONTROL 0x0d +#define CT_PANTILT_RELATIVE_CONTROL 0x0e +#define CT_ROLL_ABSOLUTE_CONTROL 0x0f +#define CT_ROLL_RELATIVE_CONTROL 0x10 +#define CT_PRIVACY_CONTROL 0x11 + +/* Processing Unit controls */ +#define PU_CONTROL_UNDEFINED 0x00 +#define PU_BACKLIGHT_COMPENSATION_CONTROL 0x01 +#define PU_BRIGHTNESS_CONTROL 0x02 +#define PU_CONTRAST_CONTROL 0x03 +#define PU_GAIN_CONTROL 0x04 +#define PU_POWER_LINE_FREQUENCY_CONTROL 0x05 +#define PU_HUE_CONTROL 0x06 +#define PU_SATURATION_CONTROL 0x07 +#define PU_SHARPNESS_CONTROL 0x08 +#define PU_GAMMA_CONTROL 0x09 +#define PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0a +#define PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0b +#define PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0c +#define PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0d +#define PU_DIGITAL_MULTIPLIER_CONTROL 0x0e +#define PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0f +#define PU_HUE_AUTO_CONTROL 0x10 +#define PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11 +#define PU_ANALOG_LOCK_STATUS_CONTROL 0x12 + +#define LXU_MOTOR_PANTILT_RELATIVE_CONTROL 0x01 +#define LXU_MOTOR_PANTILT_RESET_CONTROL 0x02 + +/* VideoStreaming interface controls */ +#define VS_CONTROL_UNDEFINED 0x00 +#define VS_PROBE_CONTROL 0x01 +#define VS_COMMIT_CONTROL 0x02 +#define VS_STILL_PROBE_CONTROL 0x03 +#define VS_STILL_COMMIT_CONTROL 0x04 +#define VS_STILL_IMAGE_TRIGGER_CONTROL 0x05 +#define VS_STREAM_ERROR_CODE_CONTROL 0x06 +#define VS_GENERATE_KEY_FRAME_CONTROL 0x07 +#define VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08 +#define VS_SYNC_DELAY_CONTROL 0x09 + +#define TT_VENDOR_SPECIFIC 0x0100 +#define TT_STREAMING 0x0101 + +/* Input Terminal types */ +#define ITT_VENDOR_SPECIFIC 0x0200 +#define ITT_CAMERA 0x0201 +#define ITT_MEDIA_TRANSPORT_INPUT 0x0202 + +/* Output Terminal types */ +#define OTT_VENDOR_SPECIFIC 0x0300 +#define OTT_DISPLAY 0x0301 +#define OTT_MEDIA_TRANSPORT_OUTPUT 0x0302 + +#define EXTERNAL_VENDOR_SPECIFIC 0x0400 +#define COMPOSITE_CONNECTOR 0x0401 +#define SVIDEO_CONNECTOR 0x0402 +#define COMPONENT_CONNECTOR 0x0403 + +#define UVC_ENTITY_IS_UNIT(entity) ((entity->type & 0xff00) == 0) +#define UVC_ENTITY_IS_TERM(entity) ((entity->type & 0xff00) != 0) +#define UVC_ENTITY_IS_ITERM(entity) ((entity->type & 0xff00) == ITT_VENDOR_SPECIFIC) +#define UVC_ENTITY_IS_OTERM(entity) ((entity->type & 0xff00) == OTT_VENDOR_SPECIFIC) + +/* ------------------------------------------------------------------------ + * GUIDs + */ +#define UVC_GUID_UVC_CAMERA {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} +#define UVC_GUID_UVC_OUTPUT {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02} +#define UVC_GUID_UVC_PROCESSING {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01} +#define UVC_GUID_UVC_SELECTOR {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02} + +#define UVC_GUID_LOGITECH_XU1 {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1d} +#define UVC_GUID_LOGITECH_XU2 {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1e} +#define UVC_GUID_LOGITECH_XU3 {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f} +#define UVC_GUID_LOGITECH_MOTOR {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56} +#define UVC_GUID_LOGITECH_DEV_INFO \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1e} + +#define UVC_GUID_FORMAT_MJPEG {0x4d, 0x4a, 0x50, 0x47, 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_YUY2 {0x59, 0x55, 0x59, 0x32, 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_NV12 {0x4e, 0x56, 0x31, 0x32, 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} + + +/* ------------------------------------------------------------------------ + * Driver specific constants. + */ + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0) + +/* Number of isochronous URBs. */ +#define UVC_URBS 5 +/* Maximum number of packets per isochronous URB. */ +#define UVC_MAX_ISO_PACKETS 40 +/* Maximum frame size in bytes, for sanity checking. */ +#define UVC_MAX_FRAME_SIZE (16*1024*1024) +/* Maximum number of video buffers. */ +#define UVC_MAX_VIDEO_BUFFERS 32 + +#define UVC_CTRL_TIMEOUT 300 + +/* Devices quirks */ +#define UVC_QUIRK_STATUS_INTERVAL 0x00000001 + +/* ------------------------------------------------------------------------ + * Structures. + */ + +struct uvc_device; + +/* TODO: Put the most frequently accessed fields at the beginning of + * structures to maximize cache efficiency. + */ +struct uvc_streaming_control +{ + __u16 bmHint; + __u8 bFormatIndex; + __u8 bFrameIndex; + __u32 dwFrameInterval; + __u16 wKeyFrameRate; + __u16 wPFrameRate; + __u16 wCompQuality; + __u16 wCompWindowSize; + __u16 wDelay; + __u32 dwMaxVideoFrameSize; + __u32 dwMaxPayloadTransferSize; + __u32 dwClockFrequency; + __u8 bmFramingInfo; + __u8 bPreferedVersion; + __u8 bMinVersion; + __u8 bMaxVersion; +}; + +struct uvc_menu_info +{ + __u32 index; + __u8 name[32]; +}; + +struct uvc_control_info +{ + struct list_head list; + struct list_head mappings; + + __u8 entity[16]; + __u8 index; + __u8 selector; + + __u8 size; + __u8 flags; +}; + +/* Data types for UVC control data */ +enum uvc_control_data_type +{ + UVC_CTRL_DATA_TYPE_RAW = 0, + UVC_CTRL_DATA_TYPE_SIGNED, + UVC_CTRL_DATA_TYPE_UNSIGNED, + UVC_CTRL_DATA_TYPE_BOOLEAN, + UVC_CTRL_DATA_TYPE_ENUM, + UVC_CTRL_DATA_TYPE_BITMASK, +}; + +struct uvc_control_mapping +{ + struct list_head list; + + struct uvc_control_info *ctrl; + + __u32 id; + __u8 name[32]; + __u8 entity[16]; + __u8 selector; + + __u8 size; + __u8 offset; + enum v4l2_ctrl_type v4l2_type; + enum uvc_control_data_type data_type; + + struct uvc_menu_info *menu_info; + __u32 menu_count; +}; + +struct uvc_control +{ + struct uvc_entity *entity; + struct uvc_control_info *info; + + __u8 index; /* Used to match the uvc_control entry with a uvc_control_info. */ + __u8 dirty : 1, + loaded : 1; + + __u8 *data; +}; + +struct uvc_format_desc +{ + __u8 guid[16]; + __u32 fcc; +}; + +/* The term 'entity' refers to both UVC units and UVC terminals. + * + * The type field is either the terminal type (wTerminalType in the terminal + * descriptor), or the unit type (bDescriptorSubtype in the unit descriptor). + * As the bDescriptorSubtype field is one byte long, the type value will + * always have a null MSB for units. All terminal types defined by the UVC + * specification have a non-null MSB, so it is safe to use the MSB to + * differentiate between units and terminals as long as the descriptor parsing + * code makes sur terminal types have a non-null MSB. + */ + +struct uvc_entity +{ + struct list_head list; + + __u8 id; + __u16 type; + char name[64]; + + union + { + struct + { + __u16 wObjectiveFocalLengthMin; + __u16 wObjectiveFocalLengthMax; + __u16 wOcularFocalLength; + __u8 bControlSize; + __u8 *bmControls; + } camera; + + struct + { + __u8 bSourceID; + } output; + + struct + { + __u8 bSourceID; + __u16 wMaxMultiplier; + __u8 bControlSize; + __u8 *bmControls; + __u8 bmVideoStandards; + } processing; + + struct + { + __u8 bNrInPins; + __u8 *baSourceID; + } selector; + + struct + { + __u8 guidExtensionCode[16]; + __u8 bNumControls; + __u8 bNrInPins; + __u8 *baSourceID; + __u8 bControlSize; + __u8 *bmControls; + __u8 *bmControlsType; + } extension; + }; + + unsigned int ncontrols; + struct uvc_control *controls; +}; + +struct uvc_frame +{ + __u8 bFrameIndex; + __u8 bmCapabilities; + __u16 wWidth; + __u16 wHeight; + __u32 dwMinBitRate; + __u32 dwMaxBitRate; + __u32 dwMaxVideoFrameBufferSize; + __u8 bFrameIntervalType; + __u32 dwDefaultFrameInterval; + __u32 *dwFrameInterval; +}; + +struct uvc_format +{ + __u8 type; + __u8 index; + __u8 bpp; + __u8 colorspace; + __u32 fcc; + __u32 flags; + + char name[32]; + + unsigned int nframes; + struct uvc_frame *frame; +}; + +struct uvc_input_header +{ + __u8 bNumFormats; + __u8 bEndpointAddress; + __u8 bmInfo; + __u8 bTerminalLink; + __u8 bStillCaptureMethod; + __u8 bTriggerSupport; + __u8 bTriggerUsage; + __u8 bControlSize; + __u8 *bmaControls; +}; + +struct uvc_output_header +{ +}; + +struct uvc_streaming +{ + struct list_head list; + + struct usb_interface *intf; + int intfnum; + __u16 maxpsize; + + union + { + struct uvc_input_header input; + struct uvc_output_header output; + }; + + unsigned int nformats; + struct uvc_format *format; + + struct uvc_streaming_control ctrl; + struct uvc_format *cur_format; + struct uvc_frame *cur_frame; + + struct mutex mutex; +}; + +enum uvc_stream_state +{ + UVC_STREAM_OFF = 0, + UVC_STREAM_INTERRUPT = 1, + UVC_STREAM_ON = 2, +}; + +enum uvc_buffer_state +{ + UVC_BUF_STATE_IDLE = 0, + UVC_BUF_STATE_QUEUED = 1, + UVC_BUF_STATE_ACTIVE = 2, + UVC_BUF_STATE_DONE = 3, + UVC_BUF_STATE_ERROR = 4, +}; + +struct uvc_buffer +{ + unsigned int size; + unsigned long vma_use_count; + struct list_head stream; + + /* Touched by interrupt handler. */ + struct v4l2_buffer buf; + struct list_head queue; + wait_queue_head_t wait; + enum uvc_buffer_state state; +}; + +struct uvc_video_queue +{ + void *mem; + unsigned int streaming; + __u32 sequence; + __u8 last_fid; + + unsigned int count; + struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS]; + struct mutex mutex; + spinlock_t irqlock; + + struct list_head mainqueue; + struct list_head irqqueue; +}; + +struct uvc_video_device +{ + struct uvc_device *dev; + struct video_device *vdev; + atomic_t active; + + enum uvc_stream_state stream; + + struct uvc_entity *iterm; + struct uvc_entity *oterm; + struct uvc_entity *processing; + struct uvc_entity *extension[8]; + struct mutex ctrl_mutex; + + struct uvc_video_queue queue; + + struct uvc_streaming *streaming; + + struct urb *urb[UVC_URBS]; + char *urb_buffer[UVC_URBS]; +}; + +enum uvc_device_state +{ + UVC_DEV_DISCONNECTED = 1, +}; + +struct uvc_device +{ + struct usb_device *udev; + struct usb_interface *intf; + __u32 quirks; + int intfnum; + + enum uvc_device_state state; + struct kref kref; + struct list_head list; + + /* Video control interface */ + __u16 uvc_version; + __u32 clock_frequency; + + struct list_head entities; + + struct uvc_video_device video; + + /* Status Interrupt Endpoint */ + struct usb_host_endpoint *int_ep; + struct urb *int_urb; + __u8 status[16]; + + /* Video Streaming interfaces */ + struct list_head streaming; +}; + +enum uvc_handle_state +{ + UVC_HANDLE_PASSIVE = 0, + UVC_HANDLE_ACTIVE = 1, +}; + +struct uvc_fh +{ + struct uvc_video_device *device; + enum uvc_handle_state state; +}; + +struct uvc_driver +{ + struct usb_driver driver; + + struct mutex open_mutex; /* protects from open/disconnect race */ + + struct list_head devices; /* struct uvc_device list */ + struct list_head controls; /* struct uvc_control_info list */ + struct mutex ctrl_mutex; /* protects controls and devices lists */ +}; + +/* ------------------------------------------------------------------------ + * Debugging, printing and logging + */ + +#define UVC_TRACE_PROBE (1 << 0) +#define UVC_TRACE_DESCR (1 << 1) +#define UVC_TRACE_CONTROL (1 << 2) +#define UVC_TRACE_FORMAT (1 << 3) +#define UVC_TRACE_CAPTURE (1 << 4) +#define UVC_TRACE_CALLS (1 << 5) +#define UVC_TRACE_IOCTL (1 << 6) +#define UVC_TRACE_FRAME (1 << 7) + +extern unsigned int uvc_trace_param; + +#define uvc_trace(flag, msg...) \ + do { \ + if (uvc_trace_param & flag) \ + printk(KERN_DEBUG "uvcvideo: " msg); \ + } while(0) + +#define uvc_printk(level, msg...) \ + printk(level "uvcvideo: " msg) + +#define UVC_GUID_FORMAT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" +#define UVC_GUID_ARGS(guid) \ + (guid)[3], (guid)[2], (guid)[1], (guid)[0], \ + (guid)[5], (guid)[4], \ + (guid)[7], (guid)[6], \ + (guid)[8], (guid)[9], \ + (guid)[10], (guid)[11], (guid)[12], \ + (guid)[13], (guid)[14], (guid)[15] + +/* -------------------------------------------------------------------------- + * Internal functions. + */ + +/* Core driver */ +extern struct uvc_driver uvc_driver; +extern void uvc_delete(struct kref *kref); + +/* Video buffers queue management. */ +extern void uvc_queue_init(struct uvc_video_queue *queue); +extern int uvc_alloc_buffers(struct uvc_video_queue *queue, + unsigned int nbuffers, unsigned int buflength); +extern int uvc_free_buffers(struct uvc_video_queue *queue); +extern void uvc_query_buffer(struct uvc_buffer *buf, + struct v4l2_buffer *v4l2_buf); +extern int uvc_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf); +extern int uvc_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf, int nonblocking); +extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable); +extern void uvc_queue_cancel(struct uvc_video_queue *queue); +extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf); + +/* V4L2 interface */ +extern struct file_operations uvc_fops; + +/* Video */ +extern int uvc_video_init(struct uvc_video_device *video); +extern int uvc_video_enable(struct uvc_video_device *video, int enable); +extern int uvc_probe_video(struct uvc_video_device *video, + struct uvc_streaming_control *probe); +extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size); +extern int uvc_set_video_ctrl(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl, int probe); +extern int uvc_init_status(struct uvc_device *dev); + +/* Controls */ +extern struct uvc_control *uvc_find_control(struct uvc_video_device *video, + __u32 v4l2_id, struct uvc_control_mapping **mapping); +extern int uvc_query_v4l2_ctrl(struct uvc_video_device *video, + struct v4l2_queryctrl *v4l2_ctrl); + +extern void uvc_ctrl_add_info(struct uvc_control_info *info); +extern int uvc_ctrl_init_device(struct uvc_device *dev); +extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); +extern void uvc_ctrl_init(void); + +extern int uvc_ctrl_begin(struct uvc_video_device *video); +extern int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback); +static inline int uvc_ctrl_commit(struct uvc_video_device *video) +{ + return __uvc_ctrl_commit(video, 0); +} +static inline int uvc_ctrl_rollback(struct uvc_video_device *video) +{ + return __uvc_ctrl_commit(video, 1); +} + +extern int uvc_ctrl_get(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl); +extern int uvc_ctrl_set(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl); + +/* Utility functions */ +extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, + unsigned int n_terms, unsigned int threshold); +extern uint32_t uvc_fraction_to_interval(uint32_t numerator, + uint32_t denominator); +extern struct usb_host_endpoint *uvc_find_endpoint( + struct usb_host_interface *alts, __u8 epaddr); + +#endif /* __KERNEL__ */ + +#endif + diff --git a/vpx_network.c b/vpx_network.c new file mode 100644 index 0000000..e5a3236 --- /dev/null +++ b/vpx_network.c
@@ -0,0 +1,2480 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "tctypes.h" +#include "rtp.h" +#include <stdio.h> +#include <ctype.h> //for tolower +#include <string.h> + +#if defined(_WIN32_WCE) && defined(WIN32_PLATFORM_WFSP) +//INITGUID needs to be defined before DEFINE_GUID is defined +# define INITGUID +#endif + +#include "vpx_network.h" + +#if ENABLE_LOGGING +# ifndef vpx_NO_GLOBALS +static int module_loglevel = 0; +# else +# include "vpx_global_handling.h" +# define module_loglevel VPXGLOBALm(vpx_network,module_loglevel) +# endif +#endif + +#ifndef INVALID_SOCKET +# define INVALID_SOCKET -1 +#endif + +#ifndef SOCKET_ERROR +# define SOCKET_ERROR -1 +#endif + +#if vpx_NET_SUPPORT_IPV6 +# define supported_network_layer(n) ((n == vpx_IPv4) || (n == vpx_IPv6)) +#else +# define supported_network_layer(n) (n == vpx_IPv4) +#endif + +#define supported_transport_layer(t) ((t == vpx_TCP) || (t == vpx_UDP)) + +#if defined(__SYMBIAN32__) +# include <in_sock.h> +/*the prototype for bzero appears in symbian's headers, but bzero is + not in the libs (Series60 v2.1)*/ +# define bzero(p,s) memset(p,0,s) +#endif + +#if defined(_WIN32_WCE) && defined(WIN32_PLATFORM_WFSP) +# include <connmgr.h> +static HANDLE g_cxhandle = NULL; //data connection handle +#endif + +enum +{ + kInited = 0x01, + kBound = 0x02, + kConnected = 0x04, + kListening = 0x08 +}; + +static TCRV socket_option(struct vpxsocket *vpx_sock, tc8 set, tc32 level, + tc32 option, void *value, tc32 optlen); + +static tc32 set_nonblocking_io(struct vpxsocket *vpx_sock, tc32 on); + +/* + * + * Exposed library functions + * +*/ + +/* + vpx_net_init() + Performs any necessary system dependent network initialization + Return: TC_OK on success, TC_ERROR otherwise +*/ +TCRV vpx_net_init() +{ + TCRV rv = TC_OK; +#if defined(WIN32) || defined(_WIN32_WCE) + WSADATA wsa_data; +#endif + + //module_loglevel = VPXLOG_DEBUG; + + vpxlog_dbg(LOG_PACKET,"vpx_network version: %s\n", vpx_network_version); + +#if defined(WIN32) || defined(_WIN32_WCE) + //smartphone +# if defined(WIN32_PLATFORM_WFSP) + + if (!g_cxhandle) + { + CONNMGR_CONNECTIONINFO ci = {0}; + DWORD status; + HANDLE connection; + HRESULT hr; + + ci.cbSize = sizeof(CONNMGR_CONNECTIONINFO); + ci.dwParams = CONNMGR_PARAM_GUIDDESTNET; //| CONNMGR_PARAM_MINRCVBW +#ifdef _X86_ + /*the emulator has a proxy connection setup to access the internet; + w/o this flag set the call will fail*/ + ci.dwFlags = CONNMGR_FLAG_PROXY_HTTP; +#else + ci.dwFlags = 0; +#endif + ci.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE; + ci.bExclusive = FALSE; //share connection among apps + ci.bDisabled = FALSE; + ci.guidDestNet = IID_DestNetCorp; + hr = ConnMgrEstablishConnectionSync(&ci, &connection, 50000, &status); + + if (hr == E_FAIL && status == CONNMGR_STATUS_NOPATHTODESTINATION) + { + vpxlog_error("vpx_net_init: connection to IID_DestNetCorp failed." + " attempting DestNetInternet\n"); + ci.guidDestNet = IID_DestNetInternet; + hr = ConnMgrEstablishConnectionSync(&ci, &connection, 50000, &status); + } + + if (hr == S_OK && status == CONNMGR_STATUS_CONNECTED) + { + vpxlog_info("vpx_net_init: ConnMgrEstablishConnectionSync success\n"); + } + else + { + rv = TC_ERROR; + vpxlog_error("vpx_net_init: ConnMgrEstablishConnectionSync failed," + " hr:%u status:%u\n", hr, status); + } + } + +# endif + + /* + Initialize windows networking services + WINSOCK_VERSION is defined in winsock2.h + if the initialization fails or the returned version + is not equal to what we asked for, return an error + */ + if (WSAStartup(WINSOCK_VERSION, &wsa_data) || + (LOBYTE(wsa_data.wVersion) != LOBYTE(WINSOCK_VERSION)) || + (HIBYTE(wsa_data.wVersion) != HIBYTE(WINSOCK_VERSION))) + { + WSACleanup(); + rv = TC_ERROR; + } + +#endif + + return rv; +} + +/* + vpx_net_destroy() + Performs any necessary system dependent network deinitialization +*/ +void vpx_net_destroy() +{ +#if defined(WIN32) || defined(_WIN32_WCE) +# if defined(WIN32_PLATFORM_WFSP) + + if (g_cxhandle) + { + //release our data connection immediately + ConnMgrReleaseConnection(g_cxhandle, 0); + g_cxhandle = NULL; + } + +# endif + WSACleanup(); +#endif +} + + +/* + vpx_net_open(struct vpxsocket* vpx_sock, enum network_layer net_layer, + enum transport_layer trans_layer) + vpx_sock - pointer to an vpxsocket structure that is to hold network info + net_layer - network layer of the socket to be created + trans_layer - transport layer of the socket to be created + Attempts to create a socket with the specified network + and transport layer. Read and send timeouts default to vpx_NET_NO_TIMEOUT. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL or if the network/transport + layer is not supported. + TC_ERROR: if a socket could not be created. +*/ +TCRV vpx_net_open(struct vpxsocket *vpx_sock, + enum network_layer net_layer, + enum transport_layer trans_layer) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && supported_transport_layer(trans_layer) && + supported_network_layer(net_layer)) + { + + memset(vpx_sock, 0, sizeof(struct vpxsocket)); + + vpx_sock->nl = net_layer; + vpx_sock->tl = trans_layer; + + switch (vpx_sock->nl) + { + case vpx_IPv4: + vpx_sock->sock = socket(PF_INET, trans_layer, 0); + break; + case vpx_IPv6: +#if vpx_NET_SUPPORT_IPV6 + vpx_sock->sock = socket(PF_INET6, trans_layer, 0); +#endif + break; + } + + if (vpx_sock->sock != INVALID_SOCKET) + { + rv = TC_OK; + vpx_sock->state = kInited; + vpx_sock->read_timeout_ms = vpx_NET_NO_TIMEOUT; + vpx_sock->send_timeout_ms = vpx_NET_NO_TIMEOUT; + + } + else + rv = TC_ERROR; + } + + + return rv; +} + +/* + vpx_net_close(struct vpxsocket* vpx_sock) + vpx_sock - pointer to an vpxsocket structure which + holds the socket to be closed + Attempts to close the socket associated with the vpx_sock structure + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL + TC_ERROR: if the socket could not be closed +*/ +TCRV vpx_net_close(struct vpxsocket *vpx_sock) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kInited)) + { +#if defined(WIN32) || defined(_WIN32_WCE) + rv = closesocket(vpx_sock->sock) ? TC_ERROR : TC_OK; +#else + int ret; + + ret = close(vpx_sock->sock); + rv = (ret) ? TC_ERROR : TC_OK; + vpxlog_dbg(LOG_PACKET, "vpx_net_close: fd=%d ret=%d eno=%d\n", + vpx_sock->sock, ret, errno); +#endif + + vpx_sock->state = 0; + } + + return rv; +} + +/* + vpx_net_bind(struct vpxsocket* vpx_sock, + union vpx_sockaddr_x* vpx_sa_x, + tcu16 port) + vpx_sock - pointer to an vpxsocket structure that contains + the socket to be bound + vpx_sa_x - pointer to an vpx_sockaddr_x struct that contains the + interface to bind to or NULL if the user wants to + bind to any interface. + port - the port to bind the socket to + Attempts to bind vpx_sock to port on the interface specified in vpx_sa_x + or to any interface if vpx_sa_x is NULL. If provided vpx_sa_x should have + been filled out by vpx_net_get_addr_info. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL or does not point to a structure + that was initialized via vpx_net_open + TC_ERROR: if vpx_sock could not be bound to the specified port + and interface +*/ +TCRV vpx_net_bind(struct vpxsocket *vpx_sock, + union vpx_sockaddr_x *vpx_sa_x, + tcu16 port) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kInited)) + { + + tc32 ret = SOCKET_ERROR; + + switch (vpx_sock->nl) + { + case vpx_IPv4: + vpx_sock->local_addr.sa_in.sin_family = AF_INET; + vpx_sock->local_addr.sa_in.sin_addr.s_addr = vpx_sa_x ? + vpx_sa_x->sa_in.sin_addr.s_addr : + INADDR_ANY; + vpx_sock->local_addr.sa_in.sin_port = htons(port); + + ret = bind(vpx_sock->sock, (struct sockaddr *)&vpx_sock->local_addr.sa_in, + sizeof(struct sockaddr_in)); + break; + case vpx_IPv6: +#if vpx_NET_SUPPORT_IPV6 + vpx_sock->local_addr.sa_in6.sin6_family = AF_INET6; + vpx_sock->local_addr.sa_in6.sin6_addr = vpx_sa_x ? + vpx_sa_x->sa_in6.sin6_addr : + in6addr_any; + vpx_sock->local_addr.sa_in6.sin6_port = htons(port); + + ret = bind(vpx_sock->sock, (struct sockaddr *)&vpx_sock->local_addr.sa_in6, + sizeof(struct sockaddr_in6)); +#endif + break; + } + + if (ret != SOCKET_ERROR) + { + vpx_sock->state |= kBound; + rv = TC_OK; + } + else + rv = TC_ERROR; + } + + return rv; +} + +/* + vpx_net_listen(struct vpxsocket* vpx_sock, tc32 backlog) + vpx_sock - pointer to a properly initialized and bound vpxsocket + structure to be setup to listen for incoming connections + backlog - the maximum length the queue of pending connections can grow to. + If the value is less than 1, backlog will be set to the system's + maximum value. + Attempts to put vpx_sock into a state where it can accept incoming connections. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, not properly initialized and bound, + or if the transport layer does not support listening + TC_ERROR: if the vpx_sock could not be put into the listening state +*/ +TCRV vpx_net_listen(struct vpxsocket *vpx_sock, tc32 backlog) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kBound) && (vpx_sock->tl == vpx_TCP)) + { + + if (backlog < 1) + backlog = SOMAXCONN; + + if (listen(vpx_sock->sock, backlog) != SOCKET_ERROR) + { + rv = TC_OK; + vpx_sock->state |= kListening; + } + else + rv = TC_ERROR; + } + + return rv; +} + +/* + vpx_net_accept(struct vpxsocket* vpx_sock, struct vpxsocket* vpx_sock_peer) + vpx_sock - pointer to a properly initialized, bound and listening + vpxsocket structure + vpx_sock_peer - result parameter; pointer to an vpxsocket structure that + will be filled out with the accepted connection's info + Attempts to have vpx_sock accept connections on the port it's currently + listening. If successful, vpx_sock_peer will be filled out with the + remote peer's info. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not put into the listening + state via vpx_net_listen or vpx_sock_peer is NULL + TC_ERROR: if there was an error accepting connections on vpx_sock +*/ +TCRV vpx_net_accept(struct vpxsocket *vpx_sock, struct vpxsocket *vpx_sock_peer) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kListening) && vpx_sock_peer) + { + + unsigned int len; + + switch (vpx_sock->nl) + { + case vpx_IPv4: + len = sizeof(struct sockaddr_in); + + vpx_sock_peer->sock = accept(vpx_sock->sock, + (struct sockaddr *)&vpx_sock_peer->remote_addr.sa_in, + &len); + break; + case vpx_IPv6: +#if vpx_NET_SUPPORT_IPV6 + len = sizeof(struct sockaddr_in6); + + vpx_sock_peer->sock = accept(vpx_sock->sock, + (struct sockaddr *)&vpx_sock_peer->remote_addr.sa_in6, + &len); +#endif + break; + } + + if (vpx_sock_peer->sock != INVALID_SOCKET) + { + + vpx_sock_peer->nl = vpx_sock->nl; + vpx_sock_peer->tl = vpx_sock->tl; + vpx_sock_peer->state = kInited | kConnected; + vpx_sock_peer->read_timeout_ms = vpx_NET_NO_TIMEOUT; + vpx_sock_peer->send_timeout_ms = vpx_NET_NO_TIMEOUT; + + rv = TC_OK; + } + else + rv = TC_ERROR; + } + + return rv; +} + +/* + vpx_net_connect(struct vpxsocket* vpx_sock, tc8* ip_addr, tcu16 port) + vpx_sock - pointer to an vpxsocket structure that is to be connencted + to the endpoint described by ip_addr and port + ip_addr - pointer to a character string that contains the address to + attempt to connect to + port - the port to attempt to connect to on ip_addr + Attempt to connect vpx_sock to port on ip_addr. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, not properly initialized or + if ip_addr is NULL + TC_ERROR: if information could not be obtained about the host or + if a connection could not be established +*/ +TCRV vpx_net_connect(struct vpxsocket *vpx_sock, tc8 *ip_addr, tcu16 port) +{ + TCRV rv = TC_INVALID_PARAMS; + + + if (vpx_sock && (vpx_sock->state & kInited)) + { + + rv = vpx_net_get_addr_info(ip_addr, port, vpx_sock->nl, vpx_sock->tl, &vpx_sock->remote_addr); + + if (!rv) + { + tc32 ret = 0; + + switch (vpx_sock->nl) + { + case vpx_IPv4: + ret = connect(vpx_sock->sock, + (struct sockaddr *)&vpx_sock->remote_addr.sa_in, + sizeof(struct sockaddr_in)); + break; + case vpx_IPv6: +#if vpx_NET_SUPPORT_IPV6 + ret = connect(vpx_sock->sock, + (struct sockaddr *)&vpx_sock->remote_addr.sa_in6, + sizeof(struct sockaddr_in6)); +#endif + break; + } + + if (!ret) + { + rv = TC_OK; + vpx_sock->state |= kConnected; + } + else + rv = TC_ERROR; + } + } + + return rv; +} + +/* + vpx_net_get_addr_info(tc8* ip_addr, + tcu16 port, + enum network_layer net_layer, + enum transport_layer trans_layer, + union vpx_sockaddr_x* vpx_sa_x) + ip_addr - address to resolve + port - port on ip_addr to obtain information for or 0 indicating any + net_layer - network layer desired on the host machine + trans_layer - the transport layer desired on the host machine + vpx_sa_x - pointer to an vpx_sockaddr_x union that will receive host + information if obtained + Attempts to acquire information about ip_addr that can be used in a + connection attempt. This information will be stored in vpx_sa_x which + can be used in subsequent vpx_net_ functions + Return: + TC_OK: on success + TC_INVALID_PARAMS: if ip_addr or vpx_sa_x are NULL or if the specified + network/transport layers are unsupported by this library + TC_ERROR: if no information could be obtained about the host +*/ +TCRV vpx_net_get_addr_info(tc8 *ip_addr, + tcu16 port, + enum network_layer net_layer, + enum transport_layer trans_layer, + union vpx_sockaddr_x *vpx_sa_x) +{ + TCRV rv = TC_INVALID_PARAMS; + + + if (ip_addr && vpx_sa_x && + supported_network_layer(net_layer) && + supported_transport_layer(trans_layer)) + { + +#if vpx_NET_SUPPORT_IPV6 + char portstr[6]; + struct addrinfo hints; + struct addrinfo *host = NULL; + + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_socktype = trans_layer; + + switch (net_layer) + { + case vpx_IPv4: + hints.ai_family = PF_INET; + break; + case vpx_IPv6: + hints.ai_family = PF_INET6; + break; + } + + sprintf(portstr, "%hu", port); + + if (!getaddrinfo(ip_addr, portstr, &hints, &host)) + { + + memcpy(&vpx_sa_x->sa_in6, host->ai_addr, host->ai_addrlen); + freeaddrinfo(host); + rv = TC_OK; + } + else + rv = TC_ERROR; + +#else + + tc8 *p = ip_addr; + tc8 is_host_name = 0; + + //IPv4, unfortunately gethostbyname will fail if given a dotted decimal ip, + //unlike getaddrinfo. So to avoid any delay's we check to see what + //representation of an ip we have, + //if there's a letter assume we have a hostname + while (*p && !(is_host_name = (tolower(*p) >= 'a' && tolower(*p) <= 'z'))) ++p; + + if (is_host_name) + { +#if defined(__SYMBIAN32__) && !defined(__WINS__) + //vpxlog_dbg("Trying to get address by name %s:%d\n", ip_addr, port); + { + + RSocketServ iSocketServ; + TNameEntry iNameEntry; + TNameRecord iNameRecord; + RHostResolver iResolver; + TRequestStatus iStatus; + + User::LeaveIfError(iSocketServ.Connect()); + User::LeaveIfError(iResolver.Open(iSocketServ, KAfInet, KProtocolInetUdp)); + + TPtrC8 ihostname8((const TUint8 *)ip_addr, strlen(ip_addr)); + TBuf<256> ihostname; + ihostname.Copy(ihostname8); + //vpxlog_dbg("ihostname = %s\n", ihostname.Ptr()); + iResolver.GetByName(ihostname, iNameEntry, iStatus); + User::WaitForRequest(iStatus); + + if (iStatus == KErrNone) + { + iNameRecord = iNameEntry(); + TInetAddr addr; + addr = TInetAddr::Cast(iNameRecord.iAddr); + bzero(&vpx_sa_x->sa_in, sizeof(struct sockaddr_in)); + vpx_sa_x->sa_in.sin_addr.s_addr = ntohl(addr.Address()); + vpx_sa_x->sa_in.sin_family = AF_INET; + vpx_sa_x->sa_in.sin_port = htons(port); + rv = TC_OK; + //vpxlog_dbg("I got an address of %x\n",vpx_sa_x->sa_in.sin_addr.s_addr); + + } + else + { + rv = TC_ERROR; + //vpxlog_dbg("I got an error message %d\n",iStatus.Int() ); + + } + + iResolver.Close(); + iSocketServ.Close(); + + } +#else + +#if defined(VXWORKS) + tc32 host; + + /* The only way VxWorks will be able to resolve a host name + is if it's in its host table or if the image was built with + the resolvLib included */ + if ((host = hostGetByName(ip_addr)) != ERROR) + { + vpx_sa_x->sa_in.sin_addr.s_addr = host; +#else + struct hostent *host; + + if ((host = gethostbyname(ip_addr))) + { + memcpy(&vpx_sa_x->sa_in.sin_addr, host->h_addr_list[0], sizeof(struct in_addr)); +#endif + vpx_sa_x->sa_in.sin_family = AF_INET; + vpx_sa_x->sa_in.sin_port = htons(port); + rv = TC_OK; + } + else + { + //could not resolve host name + rv = TC_ERROR; + } + + //vpxlog_dbg("gethostbyname returned %d %x\n", rv, host); + +#endif + + } + else + { +#if defined(__SYMBIAN32__) + bzero(&vpx_sa_x->sa_in, sizeof(struct sockaddr_in)); + + if ((vpx_sa_x->sa_in.sin_addr.s_addr = inet_addr(ip_addr)) != INADDR_NONE) + { +#elif defined(WIN32) || defined(_WIN32_WCE) + + if ((vpx_sa_x->sa_in.sin_addr.s_addr = inet_addr(ip_addr)) != INADDR_NONE) + { +#else + + if ((vpx_sa_x->sa_in.sin_addr.s_addr = inet_addr(ip_addr)) > 0) + { +#endif + vpx_sa_x->sa_in.sin_family = AF_INET; + vpx_sa_x->sa_in.sin_port = htons(port); + rv = TC_OK; + } + else + { + vpxlog_dbg(ERRORS, "vpx_network: inet_addr() call returned INADDR_NONE\n"); + rv = TC_ERROR; + } + } + +#endif + } + + return rv; +} + +/* + vpx_net_read(struct vpxsocket* vpx_sock, tc8* buffer, + tc32 buf_len, tc32* bytes_read) + vpx_sock - pointer to a properly initialized vpxsocket structure + buffer - pointer to a character array where data is to be stored + buf_len - the max max amount of data to be read into buffer + bytes_read - pointer to an integer that will receive the actual amount + of data read or NULL + Attempts to read at most buf_len bytes off the socket into buffer. This + operation can only be done on a connected socket. If a read timeout has + been set to a non-zero value the operation will fail if it could not be + completed within the specified time. If the read timeout has been set to + 0 the operation will fail if it could not be completed immediately. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized + via vpx_net_open, was not connected via + vpx_net_connect, buffer is NULL or buf_len is <= 0 + TC_TIMEDOUT: if a read timeout has been set to non-zero value and the + operation could not be completed in the specified time + TC_WOULDBLOCK: if the read timeout has been set to 0 and the operation + could not be completed immediately + TC_ERROR: if an error other than timed out or would block is encountered + trying to complete the operation, more information can be + obtained through calling vpx_net_get_error +*/ +TCRV vpx_net_read(struct vpxsocket *vpx_sock, tc8 *buffer, + tc32 buf_len, tc32 *bytes_read) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kConnected) && buffer && (buf_len > 0)) + { + + tc32 total_bytes_read = 0, + ret; + + if (vpx_sock->read_timeout_ms) + { + struct timeval tv; + fd_set read_fds; + + FD_ZERO(&read_fds); + FD_SET(vpx_sock->sock, &read_fds); + + if (vpx_sock->read_timeout_ms != vpx_NET_NO_TIMEOUT) + { + tv.tv_sec = 0; + tv.tv_usec = vpx_sock->read_timeout_ms * 1000; + +#if !defined(__SYMBIAN32__) + ret = select(vpx_sock->sock + 1, &read_fds, NULL, NULL, &tv); +#else + /*jwz 2005-10-04, symbian seems only to define a blocking select + via ioctl; as the client doesn't currently hit this path I + haven't looked for a replacement*/ + ret = -1; + vpxassert(vpx_sock->read_timeout_ms == vpx_NET_NO_TIMEOUT); +#endif + } + else + { +#if !defined(__SYMBIAN32__) + ret = select(vpx_sock->sock + 1, &read_fds, NULL, NULL, NULL); +#else + /*jwz 2005-10-04, symbian seems only to define a blocking select + via ioctl; this select was unnecessary as the socket is set to + blocking.*/ + ret = 1; +#endif + } + + if (ret > 0) + { + tc32 n = 0; + +#if defined(WIN32) || defined(_WIN32_WCE) + unsigned long recv_len = 0; + ioctlsocket(vpx_sock->sock, FIONREAD, &recv_len); +#elif defined(VXWORKS) + tcu32 recv_len = 0; + ioctl(vpx_sock->sock, FIONREAD, (tc32)&recv_len); +#elif defined(__SYMBIAN32__) + tcu32 recv_len = 0; + ioctl(vpx_sock->sock, E32IONREAD, &recv_len); +#else + tcu32 recv_len = 0; + ioctl(vpx_sock->sock, FIONREAD, &recv_len); +#endif + + /* At this point select has said that the socket is readable. + We want to try to read once before checking the amount + available on the socket in case there is an error pending, + in which case recv_len would be 0. */ + do + { + n = recv(vpx_sock->sock, buffer + total_bytes_read, buf_len, 0); + total_bytes_read += n; + buf_len -= n; + } + while (buf_len && ((tcu32)total_bytes_read < recv_len) && (n > 0)); + + rv = (n <= 0) ? TC_ERROR : TC_OK; + + } + else if (ret < 0) + rv = TC_ERROR; + else + rv = TC_TIMEDOUT; + + } + else + { + + //set the socket to non-blocking... + if (!set_nonblocking_io(vpx_sock, 1)) + { + +#if defined(WIN32) || defined(_WIN32_WCE) + tc32 error = 0; +#endif + + total_bytes_read = recv(vpx_sock->sock, buffer, buf_len, 0); + + if (total_bytes_read < 0) + { + +#if defined(LINUX) || defined(VXWORKS) || defined(__uClinux__) || defined(__SYMBIAN32__) + + switch (errno) + { + case EAGAIN: +#if defined(EWOULDBLOCK) && EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + rv = TC_WOULDBLOCK; + break; + case EINTR: + case EFAULT: + rv = TC_ERROR; + break; + default: + /* default return is TC_INVALID_PARAMS and + covers EBADF, ENOTCONN, ENOTSOCK, EINVAL, etc */ + break; + } + +#elif defined(WIN32) || defined(_WIN32_WCE) + + switch (error = WSAGetLastError()) + { + case WSAEWOULDBLOCK: + rv = TC_WOULDBLOCK; + break; + case WSAEMSGSIZE: + rv = TC_MSG_TOO_LARGE; + break; + case WSAENETDOWN: + case WSAEFAULT: + case WSAEINTR: + case WSAEINPROGRESS: + case WSAENETRESET: + case WSAESHUTDOWN: + case WSAECONNABORTED: + case WSAETIMEDOUT: + case WSAECONNRESET: + rv = TC_ERROR; + break; + default: + /* defaults are approximately equal to those + described in the LINUX #if block */ + break; + } + +#endif + + } + else + rv = TC_OK; + + //reset the socket to blocking... + if (set_nonblocking_io(vpx_sock, 0)) + rv = TC_ERROR; + +#if defined(WIN32) || defined(_WIN32_WCE) + else + WSASetLastError(error); + +#endif + } + } + + if (bytes_read) + *bytes_read = (total_bytes_read > 0) ? total_bytes_read : 0; + } + + return rv; +} + +/* + vpx_net_recvfrom(struct vpxsocket* vpx_sock, tc8* buffer, tc32 buf_len, + tc32* bytes_read, union vpx_sockaddr_x* vpx_sa_x) + vpx_sock - pointer to a properly initialized vpxsocket structure + buffer - pointer to a character array where data is to be stored + buf_len - max amount of data to be read + bytes_read - pointer to an integer that will receive the actual amount + of data read or NULL + vpx_sa_from - pointer to a vpx_sockaddr_x union used to store the address + of the remote peer the data was received from or NULL + Attempts to read at most buf_len bytes off the socket into buffer. This + operation can be done on a connected or unconnected socket. If a + read timeout has been set to a non-zero value the operation will fail if + it could not be completed within the specified time. If the send timeout + has been set to 0 the operation will fail if it could not be completed + immediately. If data is received and vpx_sa_from is non-NULL the address + of the sender will be stored in it. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized + via vpx_net_open, buffer is NULL or buf_len is <= 0 + TC_TIMEDOUT: if a read timeout has been set to non-zero value and the + operation could not be completed in the specified time + TC_WOULDBLOCK: if the read timeout has been set to 0 and the operation + could not be completed immediately + TC_ERROR: if an error other than timed out or would block is encountered + trying to complete the operation, more information can be + obtained through calling vpx_net_get_error +*/ +TCRV vpx_net_recvfrom(struct vpxsocket *vpx_sock, tc8 *buffer, tc32 buf_len, + tc32 *bytes_read, union vpx_sockaddr_x *vpx_sa_from) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kInited) && buffer && (buf_len > 0)) + { + + tc32 n = -1; + unsigned int len; + + if (vpx_sock->read_timeout_ms) + { + tc32 ret; + fd_set read_fds; + struct timeval tv; + + FD_ZERO(&read_fds); + FD_SET(vpx_sock->sock, &read_fds); + + if (vpx_sock->read_timeout_ms != vpx_NET_NO_TIMEOUT) + { + tv.tv_sec = 0; + tv.tv_usec = vpx_sock->read_timeout_ms * 1000; + +#if !defined(__SYMBIAN32__) + ret = select(vpx_sock->sock + 1, &read_fds, NULL, NULL, &tv); +#else + /*jwz 2005-10-04, symbian seems only to define a blocking select + via ioctl; as the client doesn't currently hit this path I + haven't looked for a replacement*/ + ret = -1; + vpxassert(vpx_sock->read_timeout_ms == vpx_NET_NO_TIMEOUT); +#endif + } + else + { +#if !defined(__SYMBIAN32__) + ret = select(vpx_sock->sock + 1, &read_fds, NULL, NULL, NULL); +#else + /*jwz 2005-10-04, symbian seems only to define a blocking select + via ioctl; this select was unnecessary as the socket is set to + blocking.*/ + ret = 1; +#endif + } + + if (ret > 0) + { + switch (vpx_sock->nl) + { + case vpx_IPv4: + len = sizeof(struct sockaddr_in); + n = recvfrom(vpx_sock->sock, buffer, buf_len, 0, + (struct sockaddr *)&vpx_sock->remote_addr.sa_in, &len); +#if defined(VXWORKS) + vpx_sock->remote_addr.sa_in.sin_family = AF_INET; +#endif + break; + case vpx_IPv6: +#if vpx_NET_SUPPORT_IPV6 + len = sizeof(struct sockaddr_in6); + n = recvfrom(vpx_sock->sock, buffer, buf_len, 0, + (struct sockaddr *)&vpx_sock->remote_addr.sa_in6, &len); +#endif + break; + } + + if (n == SOCKET_ERROR) + { + if (vpx_sock->tl == vpx_UDP) + { +#if defined(LINUX) || defined(VXWORKS) || defined(__uClinux__) + + if (errno == EMSGSIZE) + //n = buf_len; + rv = TC_MSG_TOO_LARGE; + +#elif defined(WIN32) || defined(_WIN32_WCE) + + if (WSAGetLastError() == WSAEMSGSIZE) + //n = buf_len; + rv = TC_MSG_TOO_LARGE; + +#elif defined(__SYMBIAN32__) + + /*jwz 2005-10-04, symbian doesn't define EMSGSIZE. I + haven't looked for a replacement as the client + currently won't be using udp*/ + if (errno == KErrTooBig) rv = TC_MSG_TOO_LARGE; + +#endif + } + else + rv = TC_ERROR; + } + else + { + rv = TC_OK; + + if (vpx_sa_from) + { + switch (vpx_sock->nl) + { + case vpx_IPv4: + memcpy(&vpx_sa_from->sa_in, &vpx_sock->remote_addr.sa_in, + sizeof(struct sockaddr_in)); + break; + case vpx_IPv6: +#if vpx_NET_SUPPORT_IPV6 + memcpy(&vpx_sa_from->sa_in6, &vpx_sock->remote_addr.sa_in6, + sizeof(struct sockaddr_in6)); +#endif + break; + } + } + } + + } + else if (ret < 0) + rv = TC_ERROR; + else + rv = TC_TIMEDOUT; + + } + else + { + + //set the socket to non-blocking... + if (!set_nonblocking_io(vpx_sock, 1)) + { + +#if defined(WIN32) || defined(_WIN32_WCE) + tc32 error = 0; +#endif + + switch (vpx_sock->nl) + { + case vpx_IPv4: + len = sizeof(struct sockaddr_in); + n = recvfrom(vpx_sock->sock, buffer, buf_len, 0, + (struct sockaddr *)&vpx_sock->remote_addr.sa_in, &len); +#if defined(VXWORKS) + vpx_sock->remote_addr.sa_in.sin_family = AF_INET; +#endif + break; + case vpx_IPv6: +#if vpx_NET_SUPPORT_IPV6 + len = sizeof(struct sockaddr_in6); + n = recvfrom(vpx_sock->sock, buffer, buf_len, 0, + (struct sockaddr *)&vpx_sock->remote_addr.sa_in6, &len); +#endif + break; + } + + if (n < 0) + { +#if defined(LINUX) || defined(VXWORKS) || defined(__uClinux__) || defined(__SYMBIAN32__) + + switch (errno) + { + case EAGAIN: +#if defined(EWOULDBLOCK) && EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + rv = TC_WOULDBLOCK; + break; + case EINTR: + case EFAULT: + rv = TC_ERROR; + break; + default: + /* default return is TC_INVALID_PARAMS and + covers EBADF, ENOTCONN, ENOTSOCK, EINVAL, etc */ + break; + } + +#elif defined(WIN32) || defined(_WIN32_WCE) + + switch (error = WSAGetLastError()) + { + case WSAEWOULDBLOCK: + rv = TC_WOULDBLOCK; + break; + case WSAEMSGSIZE: + rv = TC_MSG_TOO_LARGE; + break; + case WSAENETDOWN: + case WSAEFAULT: + case WSAEINTR: + case WSAEINPROGRESS: + case WSAENETRESET: + case WSAESHUTDOWN: + case WSAECONNABORTED: + case WSAETIMEDOUT: + case WSAECONNRESET: + rv = TC_ERROR; + break; + default: + /* defaults are approximately equal to those + described in the LINUX #if block */ + break; + } + +#endif + } + else + { + rv = TC_OK; + + if (vpx_sa_from) + { + switch (vpx_sock->nl) + { + case vpx_IPv4: + memcpy(&vpx_sa_from->sa_in, &vpx_sock->remote_addr.sa_in, + sizeof(struct sockaddr_in)); + break; + case vpx_IPv6: +#if vpx_SUPPORT_IPV6 + memcpy(&vpx_sa_from->sa_in6, &vpx_sock->remote_addr.sa_in6, + sizeof(struct sockaddr_in6)); +#endif + break; + } + } + } + + //reset the socket to blocking... + if (set_nonblocking_io(vpx_sock, 0)) + rv = TC_ERROR; + +#if defined(WIN32) || defined(_WIN32_WCE) + else + WSASetLastError(error); + +#endif + } + } + + if (bytes_read) + *bytes_read = n; + } + + return rv; +} + +/* + vpx_net_send(struct vpxsocket* vpx_sock, tc8* buffer, + tc32 buf_len, tc32* bytes_sent) + vpx_sock - pointer to a properly initialized vpxsocket structure + buffer - pointer to a character array containing data to be sent + buf_len - the length of the data in buffer + bytes_sent - pointer to an integer that will receive the actual amount + of data sent or NULL + Attempts to send buffer to vpx_sock's connected peer. If a send timeout + has been set to a non-zero value the operation will fail if it could not + be completed within the specified time. If the send timeout has been set + to 0 the operation will fail if it could not be completed immediately. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized + via vpx_net_open, was not connected via + vpx_net_connect, buffer is NULL or buf_len is <= 0 + TC_TIMEDOUT: if a send timeout has been set to non-zero value and the + operation could not be completed in the specified time + TC_WOULDBLOCK: if the send timeout has been set to 0 and the operation + could not be completed immediately + TC_ERROR: if an error other than timed out or would block is encountered + trying to complete the operation, more information can be + obtained through calling vpx_net_get_error +*/ +TCRV vpx_net_send(struct vpxsocket *vpx_sock, tc8 *buffer, + tc32 buf_len, tc32 *bytes_sent) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kConnected) && buffer && (buf_len > 0)) + { + + tc32 n = 0; + + if (vpx_sock->send_timeout_ms) + { + tc32 ret; + struct timeval tv; + fd_set write_fds; + + FD_ZERO(&write_fds); + FD_SET(vpx_sock->sock, &write_fds); + + if (vpx_sock->send_timeout_ms != vpx_NET_NO_TIMEOUT) + { + tv.tv_sec = 0; + tv.tv_usec = vpx_sock->send_timeout_ms * 1000; + +#if !defined(__SYMBIAN32__) + ret = select(vpx_sock->sock + 1, NULL, &write_fds, NULL, &tv); +#else + /*jwz 2005-10-04, symbian seems only to define a blocking select + via ioctl; as the client doesn't currently hit this path I + haven't looked for a replacement*/ + ret = -1; + vpxassert(vpx_sock->send_timeout_ms == vpx_NET_NO_TIMEOUT); +#endif + } + else + { +#if !defined(__SYMBIAN32__) + ret = select(vpx_sock->sock + 1, NULL, &write_fds, NULL, NULL); +#else + /*jwz 2005-10-04, symbian seems only to define a blocking select + via ioctl; this select was unnecessary as the socket is set to + blocking.*/ + ret = 1; +#endif + } + + if (ret > 0) + n = send(vpx_sock->sock, buffer, buf_len, 0); + else if (ret < 0) + rv = TC_ERROR; + else + rv = TC_TIMEDOUT; + + rv = (n == SOCKET_ERROR) ? TC_ERROR : TC_OK; + } + else + { + + //set the socket to non-blocking... + if (!set_nonblocking_io(vpx_sock, 1)) + { + +#if defined(WIN32) || defined(_WIN32_WCE) + tc32 error = 0; +#endif + + n = send(vpx_sock->sock, buffer, buf_len, 0); + + if (n < 0) + { +#if defined(LINUX) || defined(VXWORKS) || defined(__uClinux__) || defined(__SYMBIAN32__) + + switch (errno) + { + case EAGAIN: +#if defined(EWOULDBLOCK) && EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + rv = TC_WOULDBLOCK; + break; + case EFAULT: + case EINTR: +#ifdef EMSGSIZE + case EMSGSIZE: +#endif +#ifdef ENOBUFS + case ENOBUFS: +#endif + case ENOMEM: + case EPIPE: + rv = TC_ERROR; + break; + default: + /* default return is TC_INVALID_PARAMS and + covers EBADF, ENOTSOCK, EINVAL, etc */ + break; + } + +#elif defined(WIN32) || defined(_WIN32_WCE) + + switch (error = WSAGetLastError()) + { + case WSAEWOULDBLOCK: + rv = TC_WOULDBLOCK; + break; + case WSAEMSGSIZE: + rv = TC_MSG_TOO_LARGE; + break; + case WSAENETDOWN: + case WSAEACCES: + case WSAEINTR: + case WSAEINPROGRESS: + case WSAEFAULT: + case WSAENETRESET: + case WSAENOBUFS: + case WSAESHUTDOWN: + case WSAEHOSTUNREACH: + case WSAECONNABORTED: + case WSAECONNRESET: + case WSAETIMEDOUT: + rv = TC_ERROR; + break; + default: + /* defaults are approximately equal to those + described in the LINUX #if block */ + break; + } + +#endif + } + else + rv = TC_OK; + + //reset the socket to blocking... + if (set_nonblocking_io(vpx_sock, 0)) + rv = TC_ERROR; + +#if defined(WIN32) || defined(_WIN32_WCE) + else + WSASetLastError(error); + +#endif + + } + } + + if (bytes_sent) + *bytes_sent = n; + } + + return rv; +} + +/* + vpx_net_sendto(struct vpxsocket* vpx_sock, tc8* buffer, tc32 buf_len, + tc32* bytes_sent, union vpx_sockaddr_x vpx_sa_to) + vpx_sock - pointer to a properly initialized vpxsocket structure + buffer - pointer to a character array containing data to be sent + buf_len - the length of the data in buffer + bytes_sent - pointer to an integer that will receive the actual amount + of data sent or NULL + vpx_sa_to - vpx_sockaddr_x containing the address of the target + Attempts to send buffer to vpx_sockaddr_to. This operation can be done on + connected and unconnected sockets. If a send timeout has been set + to a non-zero value the operation will fail if it could not be completed + within the specified time. If the send timeout has been set to 0 the + operation will fail if it could not be completed immediately. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized + via vpx_net_open, buffer is NULL or buf_len is <= 0 + TC_TIMEDOUT: if a send timeout has been set to non-zero value and the + operation could not be completed in the specified time + TC_WOULDBLOCK: if the send timeout has been set to 0 and the operation + could not be completed immediately + TC_ERROR: if an error other than timed out or would block is encountered + trying to complete the operation, more information can be + obtained through calling vpx_net_get_error +*/ +TCRV vpx_net_sendto(struct vpxsocket *vpx_sock, tc8 *buffer, tc32 buf_len, + tc32 *bytes_sent, union vpx_sockaddr_x vpx_sa_to) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kInited) && buffer && (buf_len > 0)) + { + + tc32 n = 0; + + if (vpx_sock->send_timeout_ms) + { + tc32 ret; + fd_set write_fds; + struct timeval tv; + + FD_ZERO(&write_fds); + FD_SET(vpx_sock->sock, &write_fds); + + + if (vpx_sock->send_timeout_ms != vpx_NET_NO_TIMEOUT) + { + tv.tv_sec = 0; + tv.tv_usec = vpx_sock->send_timeout_ms * 1000; + +#if !defined(__SYMBIAN32__) + ret = select(vpx_sock->sock + 1, NULL, &write_fds, NULL, &tv); +#else + /*jwz 2005-10-04, symbian seems only to define a blocking select + via ioctl; as the client doesn't currently hit this path I + haven't looked for a replacement*/ + ret = -1; + vpxassert(vpx_sock->send_timeout_ms == vpx_NET_NO_TIMEOUT); +#endif + } + else + { +#if !defined(__SYMBIAN32__) + ret = select(vpx_sock->sock + 1, NULL, &write_fds, NULL, NULL); +#else + /*jwz 2005-10-04, symbian seems only to define a blocking select + via ioctl; this select was unnecessary as the socket is set to + blocking.*/ + ret = 1; +#endif + } + + if (ret > 0) + { + switch (vpx_sock->nl) + { + case vpx_IPv4: + n = sendto(vpx_sock->sock, buffer, buf_len, 0, + (struct sockaddr *)&vpx_sa_to.sa_in, + sizeof(struct sockaddr_in)); + break; + case vpx_IPv6: +#if vpx_SUPPORT_IPV6 + n = sendto(vpx_sock->sock, buffer, buf_len, 0, + (struct sockaddr *)&vpx_sa_to.sa_in6, + sizeof(struct sockaddr_in6)); +#endif + break; + } + + rv = (n == SOCKET_ERROR) ? TC_ERROR : TC_OK; + + } + else if (ret < 0) + { + rv = TC_ERROR; + } + else + rv = TC_TIMEDOUT; + + + } + else + { + + //set the socket to non-blocking... + if (!set_nonblocking_io(vpx_sock, 1)) + { + +#if defined(WIN32) || defined(_WIN32_WCE) + tc32 error = 0; +#endif + + switch (vpx_sock->nl) + { + case vpx_IPv4: + n = sendto(vpx_sock->sock, buffer, buf_len, 0, + (struct sockaddr *)&vpx_sa_to.sa_in, + sizeof(struct sockaddr_in)); + break; + case vpx_IPv6: +#if vpx_SUPPORT_IPV6 + n = sendto(vpx_sock->sock, buffer, buf_len, 0, + (struct sockaddr *)&vpx_sa_to.sa_in6, + sizeof(struct sockaddr_in6)); +#endif + break; + } + + if (n < 0) + { +#if defined(LINUX) || defined(VXWORKS) || defined(__uClinux__) || defined(__SYMBIAN32__) + + switch (errno) + { + case EAGAIN: +#if defined(EWOULDBLOCK) && EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + rv = TC_WOULDBLOCK; + break; + case EFAULT: + case EINTR: +#ifdef EMSGSIZE + case EMSGSIZE: +#endif +#ifdef ENOBUFS + case ENOBUFS: +#endif + case ENOMEM: + case EPIPE: + rv = TC_ERROR; + break; + default: + /* default return is TC_INVALID_PARAMS and + covers EBADF, ENOTSOCK, EINVAL, etc */ + break; + } + +#elif defined(WIN32) || defined(_WIN32_WCE) + + switch (error = WSAGetLastError()) + { + case WSAEWOULDBLOCK: + rv = TC_WOULDBLOCK; + break; + case WSAEMSGSIZE: + rv = TC_MSG_TOO_LARGE; + break; + case WSAENETDOWN: + case WSAEACCES: + case WSAEINTR: + case WSAEINPROGRESS: + case WSAEFAULT: + case WSAENETRESET: + case WSAENOBUFS: + case WSAESHUTDOWN: + case WSAEHOSTUNREACH: + case WSAECONNABORTED: + case WSAECONNRESET: + case WSAETIMEDOUT: + rv = TC_ERROR; + break; + default: + /* defaults are approximately equal to those + described in the LINUX #if block */ + break; + } + +#endif + } + else + rv = TC_OK; + + //reset the socket to blocking... + if (set_nonblocking_io(vpx_sock, 0)) + rv = TC_ERROR; + +#if defined(WIN32) || defined(_WIN32_WCE) + else + WSASetLastError(error); + +#endif + + } + } + + if (bytes_sent) + *bytes_sent = n; + } + + return rv; +} + +/* + vpx_net_is_readable(struct vpxsocket* vpx_sock) + vpx_sock - pointer to a properly initialized vpxsocket structure to + be polled to see if data can be read from it + Return: + 0: vpx_sock was NULL, did not point to a vpxsocket structure that was + initialized via vpx_net_open or the socket has no data that can be read + 1: the socket has data that can be read +*/ +tc32 vpx_net_is_readable(struct vpxsocket *vpx_sock) +{ + tc32 is_readable = 0; + + if (vpx_sock && (vpx_sock->state & kInited)) + { + tc32 ret; +#ifndef __SYMBIAN32__ + struct timeval tv = {0, 0}; + fd_set read_fds; + + FD_ZERO(&read_fds); + FD_SET(vpx_sock->sock, &read_fds); + + ret = select(vpx_sock->sock + 1, &read_fds, NULL, NULL, &tv); + + if ((ret > 0) && FD_ISSET(vpx_sock->sock, &read_fds)) + is_readable = 1; + +#else + /*jwz 2005-10-04, we do use this in the client to detect an error + condition. we'll have to see if this partial implementation is + enough; perhaps we'll have to write a wrapper for the RSocket class*/ + /*jwz 2008-01-31, not sure whether this select workaround is necessary + now, but current builds have been using it w/no visible ill effects*/ + char b; + + ret = set_nonblocking_io(vpx_sock, 1); + vpxassert(!ret); + ret = (!ret) ? recv(vpx_sock->sock, &b, 1, MSG_PEEK) : ret; + + if (ret > 0) is_readable = 1; + else if (errno != EAGAIN && errno != KErrWouldBlock/*-1000*/) + { + vpxlog_error("vpx_net_is_readable, ret=%d eno=%d\n", ret, errno); + is_readable = 1; + } + else is_readable = 0; + + ret = set_nonblocking_io(vpx_sock, 0); + vpxassert(!ret); +#endif + } + + return is_readable; +} + +/* + vpx_net_amount_readable(struct vpxsocket* vpx_sock, TCRV* rv) + vpx_sock - pointer to a properly initialized vpxsocket structure to + be polled to see if data can be read from it + rv - TCRV pointer to receive the result of the function. This parameter + may be NULL. rv will be set to TC_OK on success and TC_ERROR + on error. + Return: + The amount of data in bytes able to be read off the socket +*/ +tc32 vpx_net_amount_readable(struct vpxsocket *vpx_sock, TCRV *rv) +{ + tc32 ret; + tc32 amount = 0; + + if (vpx_sock && (vpx_sock->state & kInited)) + { +#if defined(WIN32) || defined(_WIN32_WCE) + unsigned long amt; + ret = ioctlsocket(vpx_sock->sock, FIONREAD, &amt); + amount = (tc32)amt; +#elif defined(LINUX) || defined(__uClinux__) + ret = ioctl(vpx_sock->sock, FIONREAD, &amount); +#elif defined(VXWORKS) + ret = ioctl(vpx_sock->sock, FIONREAD, (tc32)&amount); +#elif defined(__SYMBIAN32__) + ret = ioctl(vpx_sock->sock, E32IONREAD, &amount); +#endif + + if (rv) + *rv = (ret == SOCKET_ERROR) ? TC_ERROR : TC_OK; + } + + return amount; +} + +/* + vpx_net_is_writeable(struct vpxsocket* vpx_sock) + vpx_sock - pointer to a properly initialized vpxsocket structure to + be polled to see if data can be written to it + Return: + 0: vpx_sock was NULL, did not point to a vpxsocket structure that was + initialized via vpx_net_open or the socket cannot be written to + without blocking + 1: the socket can be written to without blocking +*/ +tc32 vpx_net_is_writeable(struct vpxsocket *vpx_sock) +{ + tc32 is_writeable = 0; + + if (vpx_sock && (vpx_sock->state & kInited)) + { + tc32 ret; +#if !defined(__SYMBIAN32__) + struct timeval tv = {0, 0}; +#endif + fd_set write_fds; + + FD_ZERO(&write_fds); + FD_SET(vpx_sock->sock, &write_fds); + +#if !defined(__SYMBIAN32__) + ret = select(vpx_sock->sock + 1, NULL, &write_fds, NULL, &tv); +#else + ret = 0; +#endif + + if ((ret > 0) && + (FD_ISSET(vpx_sock->sock, &write_fds))) + is_writeable = 1; + } + + return is_writeable; +} + +/* + vpx_net_set_read_timeout(struct vpxsocket* vpx_sock, tcu32 read_timeout) + vpx_sock - pointer to a properly initialized vpxsocket structure + read_timeout - time to wait in milliseconds before giving up on a read + operation. 0 indicates that a non-blocking attempt to read + should be made. vpx_NET_NO_TIMEOUT - indicates the socket + should never timeout. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock was NULL or did not point to an vpxsocket + that was initialized via vpx_net_open +*/ +TCRV vpx_net_set_read_timeout(struct vpxsocket *vpx_sock, tcu32 read_timeout) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kInited)) + { + + vpx_sock->read_timeout_ms = read_timeout; + rv = TC_OK; + + } + + return rv; +} + +/* + vpx_net_set_send_timeout(struct vpxsocket* vpx_sock, tcu32 send_timeout) + vpx_sock - pointer to a properly initialized vpxsocket structure + read_timeout - time to wait in milliseconds before giving up on a send + operation. 0 indicates that a non-blocking attempt to send + should be made. vpx_NET_NO_TIMEOUT - indicates the socket + should never timeout. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock was NULL or did not point to an vpxsocket + that was initialized via vpx_net_open +*/ +TCRV vpx_net_set_send_timeout(struct vpxsocket *vpx_sock, tcu32 send_timeout) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kInited)) + { + + vpx_sock->send_timeout_ms = send_timeout; + rv = TC_OK; + + } + + return rv; +} + +/* + vpx_net_get_error(tc32* vpx_net_errno) + vpx_net_errno - pointer to a tc32 to store the last system network + error code or NULL if the user does not want it + Return: + A string representing the last system network error that occurred. This + string can only be used until the next call to vpx_net_get_error() +*/ +tc8 *vpx_net_get_error(tc32 *vpx_net_errno) +{ +#if defined(LINUX) || defined(__uClinux__) || defined(__SYMBIAN32__) + + if (vpx_net_errno) + *vpx_net_errno = errno; + + return strerror(errno); +#elif defined(VXWORKS) + tc32 error = errno; + tc8 *errStr = "No Error"; + + switch (error) + { + case ENOTSUP: + errStr = "ENOTSUP"; + break; + case EMSGSIZE: + errStr = "EMSGSIZE"; + break; + case EDESTADDRREQ: + errStr = "EDESTADDRREQ"; + break; + case EPROTOTYPE: + errStr = "EPROTOTYPE"; + break; + case ENOPROTOOPT: + errStr = "ENOPROTOOPT"; + break; + case EPROTONOSUPPORT: + errStr = "EPROTONOSUPPORT"; + break; + case ESOCKTNOSUPPORT: + errStr = "ESOCKTNOSUPPORT"; + break; + case EOPNOTSUPP: + errStr = "EOPNOTSUPP"; + break; + case EPFNOSUPPORT: + errStr = "EPFNOSUPPORT"; + break; + case EAFNOSUPPORT: + errStr = "EAFNOSUPPORT"; + break; + case EADDRINUSE: + errStr = "EADDRINUSE"; + break; + case EADDRNOTAVAIL: + errStr = "EADDRNOTAVAIL"; + break; + case ENOTSOCK: + errStr = "ENOTSOCK"; + break; + case ENETUNREACH: + errStr = "ENETUNREACH"; + break; + case ENETRESET: + errStr = "ENETRESET"; + break; + case ECONNABORTED: + errStr = "ECONNABORTED"; + break; + case ECONNRESET: + errStr = "ECONNRESET"; + break; + case ENOBUFS: + errStr = "ENOBUFS"; + break; + case EISCONN: + errStr = "EISCONN"; + break; + case ENOTCONN: + errStr = "ENOTCONN"; + break; + case ESHUTDOWN: + errStr = "ESHUTDOWN"; + break; + case ETOOMANYREFS: + errStr = "ETOOMANYREFS"; + break; + case ETIMEDOUT: + errStr = "ETIMEDOUT"; + break; + case ECONNREFUSED: + errStr = "ECONNREFUSED"; + break; + case ENETDOWN: + errStr = "ENETDOWN"; + break; + case ETXTBSY: + errStr = "ETXTBSY"; + break; + case ELOOP: + errStr = "ELOOP"; + break; + case EHOSTUNREACH: + errStr = "EHOSTUNREACH"; + break; + case ENOTBLK: + errStr = "ENOTBLK"; + break; + case EHOSTDOWN: + errStr = "EHOSTDOWN"; + break; + } + + if (vpx_net_errno) + *vpx_net_errno = error; + + return errStr; +#elif defined(WIN32) || defined(_WIN32_WCE) + tc8 *errstr = "No error"; + tc32 error = WSAGetLastError(); + + /* there isn't much use in doing this, but it might give + enough info without having to use net helpmsg */ + switch (error) + { + case WSAEWOULDBLOCK: + errstr = "WSAEWOULDBLOCK"; + break; + case WSAEINPROGRESS: + errstr = "WSAEINPROGRESS"; + break; + case WSAEALREADY: + errstr = "WSAEALREADY"; + break; + case WSAENOTSOCK: + errstr = "WSAENOTSOCK"; + break; + case WSAEDESTADDRREQ: + errstr = "WSAEDESTADDRREQ"; + break; + case WSAEMSGSIZE: + errstr = "WSAEMSGSIZE"; + break; + case WSAEPROTOTYPE: + errstr = "WSAEPROTOTYPE"; + break; + case WSAENOPROTOOPT: + errstr = "WSAENOPROTOOPT"; + break; + case WSAEPROTONOSUPPORT: + errstr = "WSAEPROTONOSUPPORT"; + break; + case WSAESOCKTNOSUPPORT: + errstr = "WSAESOCKTNOSUPPORT"; + break; + case WSAEOPNOTSUPP: + errstr = "WSAEOPNOTSUPP"; + break; + case WSAEPFNOSUPPORT: + errstr = "WSAEPFNOSUPPORT"; + break; + case WSAEAFNOSUPPORT: + errstr = "WSAEAFNOSUPPORT"; + break; + case WSAEADDRINUSE: + errstr = "WSAEADDRINUSE"; + break; + case WSAEADDRNOTAVAIL: + errstr = "WSAEADDRNOTAVAIL"; + break; + case WSAENETDOWN: + errstr = "WSAENETDOWN"; + break; + case WSAENETUNREACH: + errstr = "WSAENETUNREACH"; + break; + case WSAENETRESET: + errstr = "WSAENETRESET"; + break; + case WSAECONNABORTED: + errstr = "WSAECONNABORTED"; + break; + case WSAECONNRESET: + errstr = "WSAECONNRESET"; + break; + case WSAENOBUFS: + errstr = "WSAENOBUFS"; + break; + case WSAEISCONN: + errstr = "WSAEISCONN"; + break; + case WSAENOTCONN: + errstr = "WSAENOTCONN"; + break; + case WSAESHUTDOWN: + errstr = "WSAESHUTDOWN"; + break; + case WSAETOOMANYREFS: + errstr = "WSAETOOMANYREFS"; + break; + case WSAETIMEDOUT: + errstr = "WSAETIMEDOUT"; + break; + case WSAECONNREFUSED: + errstr = "WSAECONNREFUSED"; + break; + case WSAELOOP: + errstr = "WSAELOOP"; + break; + case WSAENAMETOOLONG: + errstr = "WSAENAMETOOLONG"; + break; + case WSAEHOSTDOWN: + errstr = "WSAEHOSTDOWN"; + break; + case WSAEHOSTUNREACH: + errstr = "WSAEHOSTUNREACH"; + break; + case WSAENOTEMPTY: + errstr = "WSAENOTEMPTY"; + break; + case WSAEPROCLIM: + errstr = "WSAEPROCLIM"; + break; + case WSAEUSERS: + errstr = "WSAEUSERS"; + break; + case WSAEDQUOT: + errstr = "WSAEDQUOT"; + break; + case WSAESTALE: + errstr = "WSAESTALE"; + break; + case WSAEREMOTE: + errstr = "WSAEREMOTE"; + break; + case WSASYSNOTREADY: + errstr = "WSASYSNOTREADY"; + break; + case WSAVERNOTSUPPORTED: + errstr = "WSAVERNOTSUPPORTED"; + break; + case WSANOTINITIALISED: + errstr = "WSANOTINITIALISED"; + break; + case WSAEDISCON: + errstr = "WSAEDISCON"; + break; + case WSAENOMORE: + errstr = "WSAENOMORE"; + break; + case WSAECANCELLED: + errstr = "WSAECANCELLED"; + break; + case WSAEINVALIDPROCTABLE: + errstr = "WSAEINVALIDPROCTABLE"; + break; + case WSAEINVALIDPROVIDER: + errstr = "WSAEINVALIDPROVIDER"; + break; + case WSAEPROVIDERFAILEDINIT: + errstr = "WSAEPROVIDERFAILEDINIT"; + break; + case WSASYSCALLFAILURE: + errstr = "WSASYSCALLFAILURE"; + break; + case WSASERVICE_NOT_FOUND: + errstr = "WSASERVICE_NOT_FOUND"; + break; + case WSATYPE_NOT_FOUND: + errstr = "WSATYPE_NOT_FOUND"; + break; + case WSA_E_NO_MORE: + errstr = "WSA_E_NO_MORE"; + break; + case WSA_E_CANCELLED: + errstr = "WSA_E_CANCELLED"; + break; + case WSAEREFUSED: + errstr = "WSAEREFUSED"; + break; + case WSAHOST_NOT_FOUND: + errstr = "WSAHOST_NOT_FOUND"; + break; + } + + if (vpx_net_errno) + *vpx_net_errno = error; + + return errstr; +#endif +} + +/* + vpx_net_recv_buf(struct vpxsocket* vpx_sock, tc8 set, tc32* value) + vpx_sock - a pointer to a properly initialized vpxsocket structure + set - Value indicating whether the option should be set or queried. + 1 indicates the option should be set using the value stored + in value. 0 indicates the current value of the option should + be returned in value. + value - depending on the value of set, either contains the size + to set the socket's receive buffer to or will receive the + current size of the socket's receive buffer + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, wasn't initialized via + vpx_net_open or value is NULL + TC_ERROR: if the option could not be queried/set +*/ +TCRV vpx_net_recv_buf(struct vpxsocket *vpx_sock, tc8 set, tc32 *value) +{ + tc32 optlen = sizeof(tc32); + return socket_option(vpx_sock, set, SOL_SOCKET, SO_RCVBUF, value, optlen); +} + +/* + vpx_net_send_buf(struct vpxsocket* vpx_sock, tc8 set, tc32* value) + vpx_sock - a pointer to a properly initialized vpxsocket structure + set - Value indicating whether the option should be set or queried. + 1 indicates the option should be set using the value stored + in value. 0 indicates the current value of the option should + be returned in value. + value - depending on the value of set, either contains the size + to set the socket's send buffer to or will receive the + current size of the socket's send buffer + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, wasn't initialized via + vpx_net_open or value is NULL + TC_ERROR: if the option could not be queried/set +*/ +TCRV vpx_net_send_buf(struct vpxsocket *vpx_sock, tc8 set, tc32 *value) +{ + tc32 optlen = sizeof(tc32); + return socket_option(vpx_sock, set, SOL_SOCKET, SO_SNDBUF, value, optlen); +} + +/* + vpx_net_reuse_addr(struct vpxsocket* vpx_sock, tc8 set, tc32* value) + vpx_sock - a pointer to a properly initialized vpxsocket structure + set - Value indicating whether the option should be set or queried. + 1 indicates the option should be set using the value stored + in value. 0 indicates the current value of the option should + be returned in value. + value - depending on the value of set, either contains an integer + 0/1 to indicate whether the socket's reuse address option + should be turned on or off or will receive the current setting + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, wasn't initialized via + vpx_net_open or value is NULL + TC_ERROR: if the option could not be queried/set +*/ +TCRV vpx_net_reuse_addr(struct vpxsocket *vpx_sock, tc8 set, tc32 *value) +{ + tc32 optlen = sizeof(tc32); + return socket_option(vpx_sock, set, SOL_SOCKET, SO_REUSEADDR, value, optlen); +} + +/* + vpx_net_linger(struct vpxsocket* vpx_sock, tc8 set, tcu16* on, tcu16* sec) + vpx_sock - a pointer to a properly initialized vpxsocket structure + set - Value indicating whether the option should be set or queried. + 1 indicates the option should be set using the values stored in + on and sec. 0 indicates the current value of the option should + be returned in on and sec. + on - depending on the value of set indicates whether to turn on/off (1/0) + the linger option or will receive the current setting + sec - depending on the value of set indicates the amount of time in + seconds for the socket to linger or will receive the current value + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, wasn't initialized via + vpx_net_open or on or sec are NULL + TC_ERROR: if the option could not be queried/set +*/ +TCRV vpx_net_linger(struct vpxsocket *vpx_sock, tc8 set, tcu16 *on, tcu16 *sec) +{ + TCRV rv = TC_INVALID_PARAMS; + + /*on symbian SO_LINGER is defined but there's no linger struct*/ +#if !defined(__SYMBIAN32__) + tc32 optlen = sizeof(struct linger); + struct linger l; + + if (set && on && sec) + { + l.l_onoff = *on; + l.l_linger = *sec; + + rv = socket_option(vpx_sock, set, SOL_SOCKET, SO_LINGER, &l, optlen); + } + else if (on && sec) + { + rv = socket_option(vpx_sock, set, SOL_SOCKET, SO_LINGER, &l, optlen); + + *on = l.l_onoff; + *sec = l.l_linger; + } + +#else + (void)vpx_sock; + (void)set; + (void)on; + (void)sec; +#endif + + return rv; +} + + +/* udp only functions */ + +/* + vpx_net_multicast_ttl(struct vpxsocket* vpx_sock, tc8 set, tcu8* value) + vpx_sock - pointer to a properly initialized vpxsocket structure + set - flag indicating whether to set (non-zero value) or + query (0) the option + value - depending on the value of set, sets the ttl to the value stored + in value or receives the current value of ttl + Attempts to set/query the multicast ttl value of the socket + represented by vpx_sock + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not initialized via + vpx_net_open, the socket is not a udp socket or + value is NULL + TC_ERROR: if the option could not be queried/set +*/ +TCRV vpx_net_multicast_ttl(struct vpxsocket *vpx_sock, tc8 set, tcu8 *value) +{ + TCRV rv = TC_INVALID_PARAMS; + tc32 optlen = sizeof(tcu8); + + if (vpx_sock && (vpx_sock->state & kInited) && (vpx_sock->tl == vpx_UDP)) + { + switch (vpx_sock->nl) + { + case vpx_IPv4: + rv = socket_option(vpx_sock, set, IPPROTO_IP, IP_MULTICAST_TTL, value, optlen); + break; + case vpx_IPv6: +#if vpx_NET_SUPPORT_IPV6 + rv = socket_option(vpx_sock, set, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, value, optlen); +#endif + break; + } + } + + return rv; +} + +/* + vpx_net_join_multicast(struct vpxsocket* vpx_sock, + union vpx_sockaddr_x* remote_addr) + vpx_sock - pointer to a properly initialized vpxsocket structure + local_addr - an vpx_sockaddr_x structure containing the address of the + local interface to use for the multicast session or NULL + indicating any interface can be used. (Currently ignored + for IPv6). + remote_addr - an vpx_sockaddr_x structure containing the multicast address + Attempts to add vpx_sock to the multicast session indicated by remote_addr. + On success the reuse_addr option will be set on vpx_sock so others may + join the session and vpx_sock will be added to the session. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized via + vpx_net_open, vpx_sock does not represent a UDP socket, + or remote_addr is NULL + TC_ERROR: if the reuse_addr option could not be set or the socket could + not be added to the multicast session +*/ +TCRV vpx_net_join_multicast(struct vpxsocket *vpx_sock, + union vpx_sockaddr_x *local_addr, + union vpx_sockaddr_x *remote_addr) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kInited) && + (vpx_sock->tl == vpx_UDP) && remote_addr) + { + + switch (vpx_sock->nl) + { + case vpx_IPv4: + { + tc32 on = 1; + struct ip_mreq mreq; + + //allow others to bind to the same multicast port + rv = vpx_net_reuse_addr(vpx_sock, 1, &on); + + if (!rv) + { + + mreq.imr_interface.s_addr = local_addr ? + local_addr->sa_in.sin_addr.s_addr : + INADDR_ANY; + mreq.imr_multiaddr.s_addr = remote_addr->sa_in.sin_addr.s_addr; + + memset(&vpx_sock->local_addr.sa_in, 0, sizeof(struct sockaddr_in)); + memset(&vpx_sock->remote_addr.sa_in, 0, sizeof(struct sockaddr_in)); + + vpx_sock->local_addr.sa_in.sin_addr.s_addr = mreq.imr_interface.s_addr; + vpx_sock->remote_addr.sa_in.sin_addr.s_addr = mreq.imr_multiaddr.s_addr; + + rv = socket_option(vpx_sock, 1, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(struct ip_mreq)); + } + } + break; + case vpx_IPv6: +#if vpx_NET_SUPPORT_IPV6 + { + tc32 on = 1; + struct ipv6_mreq mreq; + + //allow others to bind to the same multicast port + rv = vpx_net_reuse_addr(vpx_sock, 1, &on); + + if (!rv) + { + + struct in6_addr in6 = in6addr_any; + + mreq.ipv6mr_interface = 0; + + memset(&vpx_sock->local_addr.sa_in6, 0, sizeof(struct sockaddr_in6)); + memset(&vpx_sock->remote_addr.sa_in6, 0, sizeof(struct sockaddr_in6)); + + memcpy(&mreq.ipv6mr_multiaddr, &remote_addr->sa_in6.sin6_addr, + sizeof(struct in6_addr)); + + memcpy(&vpx_sock->local_addr.sa_in6.sin6_addr, + &in6, sizeof(struct in6_addr)); + + memcpy(&vpx_sock->remote_addr.sa_in6.sin6_addr, + &mreq.ipv6mr_multiaddr, sizeof(struct in6_addr)); + + rv = socket_option(vpx_sock, 1, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, + &mreq, sizeof(struct ipv6_mreq)); + } + } +#endif + break; + } + } + + + return rv; +} + + +/* + vpx_net_join_multicast_addr(struct vpxsocket* vpx_sock + , tc8* ip_addr + , tcu16 port) + vpx_sock - pointer to an vpxsocket structure that is to be connencted + to the endpoint described by ip_addr and port + ip_addr - pointer to a character string that contains the multicast + address to attempt to join + port - the port to attempt to join on ip_addr + Attempt to join vpx_sock to port on ip_addr. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized via + vpx_net_open, vpx_sock does not represent a UDP socket, + or remote_addr is NULL + TC_ERROR: if the reuse_addr option could not be set or the socket could + not be added to the multicast session +*/ +TCRV vpx_net_join_multicast_addr(struct vpxsocket *vpx_sock + , tc8 *ip_addr + , tcu16 port) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kInited) && + (vpx_sock->tl == vpx_UDP) && ip_addr) + { + rv = vpx_net_get_addr_info(ip_addr, port, vpx_sock->nl, + vpx_sock->tl, &vpx_sock->remote_addr); + + if (!rv) + { + tc32 ret = 0; + + ret = vpx_net_bind(vpx_sock, + NULL, + port); + + if (!ret) + { + ret = vpx_net_join_multicast(vpx_sock, + NULL, + &vpx_sock->remote_addr); + + if (!ret) + { + rv = TC_OK; + vpx_sock->state |= kConnected; + } + else + rv = TC_ERROR; + } + else + { + rv = TC_ERROR; + } + } + } + + return rv; +} + +/* + vpx_net_leave_multicast(struct vpxsocket* vpx_sock) + vpx_sock - pointer to a properly initialized vpxsocket structure to be + removed from the multicast session + Attempts to remove vpx_sock from the multicast session it was previously + added to via vpx_net_join_multicast + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized via + vpx_net_open or the socket is not a UDP socket + TC_ERROR: if the socket could not be removed from the session +*/ +TCRV vpx_net_leave_multicast(struct vpxsocket *vpx_sock) +{ + TCRV rv = TC_INVALID_PARAMS; + + if (vpx_sock && (vpx_sock->state & kInited) && (vpx_sock->tl == vpx_UDP)) + { + + switch (vpx_sock->nl) + { + case vpx_IPv4: + { + struct ip_mreq mreq; + + mreq.imr_interface.s_addr = vpx_sock->local_addr.sa_in.sin_addr.s_addr; + mreq.imr_multiaddr.s_addr = vpx_sock->remote_addr.sa_in.sin_addr.s_addr; + + rv = socket_option(vpx_sock, 1, IPPROTO_IP, IP_DROP_MEMBERSHIP, + &mreq, sizeof(struct ip_mreq)); + } + break; + case vpx_IPv6: +#if vpx_NET_SUPPORT_IPV6 + { + struct ipv6_mreq mreq; + + mreq.ipv6mr_interface = 0; + + memcpy(&mreq.ipv6mr_multiaddr, &vpx_sock->remote_addr.sa_in6.sin6_addr, + sizeof(struct in6_addr)); + + rv = socket_option(vpx_sock, 1, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, + &mreq, sizeof(struct ipv6_mreq)); + } +#endif + break; + } + } + + return rv; +} + +/* END - udp only functions */ + +/* + * + * END - Exposed library functions + * +*/ + +/* + * + * Internal library functions + * +*/ + +/* + socket_option(struct vpxsocket* vpx_sock, tc8 set, tc32 level, + tc32 option, void* value, tc32 optlen) + Generic function used to wrap set/getsockopt. Used by exposed library + functions to set/get socket options +*/ +static TCRV socket_option(struct vpxsocket *vpx_sock, tc8 set, tc32 level, + tc32 option, void *value, tc32 optlen) +{ + TCRV rv = TC_INVALID_PARAMS; + tc32 ret; + + if (vpx_sock && (vpx_sock->state & kInited) && value) + { + if (set) + ret = setsockopt(vpx_sock->sock, level, option, value, optlen); + else + ret = getsockopt(vpx_sock->sock, level, option, value, (tcu32 *)&optlen); + + rv = (ret == SOCKET_ERROR) ? TC_ERROR : TC_OK; + } + + return rv; +} + +/* + set_nonblocking_io(struct vpxsocket* vpx_sock, tc32 on) + vpx_sock - pointer to an vpxsocket structure + on - turn non-blocking io on (non-zero value) / off (0) + Internal library function used to turn non-blocking io + on/off for the specified socket +*/ +static tc32 set_nonblocking_io(struct vpxsocket *vpx_sock, tc32 on) +{ +#if defined(WIN32) || defined(_WIN32_WCE) + return ioctlsocket(vpx_sock->sock, FIONBIO, &((unsigned long)on)); +#elif defined(VXWORKS) + return ioctl(vpx_sock->sock, FIONBIO, (tc32)&on); +#elif defined(__SYMBIAN32__) + /*jwz 2005-10-04, FIONBIO is undefined on symbian and I haven't looked for + a replacement. For now this is acceptable as the only use of + non-blocking io in the client is rtp(udp)*/ + //(void)vpx_sock; (void)on; + //return -1; + //const int dbg_on=on; + int ret; + + if (on) ret = setsockopt(vpx_sock->sock, SOL_SOCKET, KSONonBlockingIO, &on, sizeof(on)); + else ret = setsockopt(vpx_sock->sock, SOL_SOCKET, KSOBlockingIO, (on = 1, &on), sizeof(on)); + + //vpxlog_dbg("set_nbio(%d): ret:%d eno:%d\n",dbg_on,ret,eno); + return ret; +#else + return ioctl(vpx_sock->sock, FIONBIO, &on); +#endif +} diff --git a/vpx_network.h b/vpx_network.h new file mode 100644 index 0000000..8c20838 --- /dev/null +++ b/vpx_network.h
@@ -0,0 +1,652 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef __vpx_NETWORK_H__ +#define __vpx_NETWORK_H__ + +/* vpx_network version info */ +#define vpx_network_version "2.1.1.7" + +#define vpx_NETWORK_VERSION_CHIEF 2 +#define vpx_NETWORK_VERSION_MAJOR 1 +#define vpx_NETWORK_VERSION_MINOR 1 +#define vpx_NETWORK_VERSION_PATCH 7 +/* end - vpx_network version info */ + +#include "tctypes.h" + +#if defined(_WIN32_WCE) && _WIN32_WCE < 420 +# define WIN32_LEAN_AND_MEAN +# include <winsock.h> +# ifndef WINSOCK_VERSION +# define WINSOCK_VERSION MAKEWORD(1,1) +# endif +#elif defined(WIN32) || defined(_WIN32_WCE) +# define WIN32_LEAN_AND_MEAN +# include <winsock2.h> +# include <ws2tcpip.h> //for IPv6 structures/functions, IPPROTO_IP options +# ifdef getaddrinfo //some IPv6 calls/structures are missing without the + +//current platform sdk (2/2003) +# define vpx_NET_SUPPORT_IPV6 1 //support on 2000 is an option, but no production +//release will be made for it. If the support isn't +//there the calls will map to IPv4 calls (i.e. getaddrinfo +//to inet_addr/gethostbyname). +//Starting with XP full support was added. +//XP SP.1+/Server 2003 have a production +//implementation of IPv6. +# endif +#elif defined(LINUX) || defined(__uClinux__) || defined(__SYMBIAN32__) +# include <unistd.h> //for close if undefined elsewhere +# include <sys/types.h> +# include <sys/time.h> //for timeval if undefined elsewhere +# include <sys/ioctl.h> //for ioctl, FIONREAD +# include <sys/socket.h> //for socket(), bind()...SOCK_STREAM +# include <netinet/in.h> //sockaddr_in... +# include <arpa/inet.h> //inet_addr +# include <netdb.h> //for addrinfo +# include <errno.h> +#elif defined(VXWORKS) +# include <hostLib.h> +# include <sockLib.h> +# include <netinet/in.h> //for sockaddr_in, socket options, etc. +# include <ioLib.h> //for FIONREAD +# include <netdb.h> +#elif defined(TI_OMAP) +# define ntohl(x) htonl(x) +# define ntohs(x) htons(x) +#elif defined(vpx_NET_STUBS) +#else +# error "Network support not yet added for this platform!" +#endif + +#ifndef vpx_NET_SUPPORT_IPV6 +# define vpx_NET_SUPPORT_IPV6 0 +#endif + +#define vpx_NET_NO_TIMEOUT 0xffffffff + +#if defined(__cplusplus) +extern "C" { +#endif + +#if !defined(vpx_NET_STUBS) + + /* + Valid network layers to be passed to vpx_net_ functions. + Prefixed with vpx_ to prevent naming conflicts. + */ + enum network_layer { + vpx_IPv4, + vpx_IPv6 + }; + + /* + Valid transport layers to be passed to vpx_net_ functions. + Prefixed with vpx_ to prevent naming conflicts. + */ + enum transport_layer + { + vpx_TCP = SOCK_STREAM, + vpx_UDP = SOCK_DGRAM + }; + + /* + Union used in calls to vpx_net_ functions. + Depending on the network layer in use the correct sockaddr structure + will be chosen internally by the library. + */ + union vpx_sockaddr_x + { + struct sockaddr_in sa_in; +#if vpx_NET_SUPPORT_IPV6 + struct sockaddr_in6 sa_in6; +#endif + }; + + /* + vpx_network's socket representation, used in vpx_net_ function calls + */ + struct vpxsocket + { + +#if defined(WIN32) || defined(_WIN32_WCE) + SOCKET sock; +#else + tc32 sock; +#endif + + tc32 state; + tcu32 read_timeout_ms, + send_timeout_ms; + + enum network_layer nl; + enum transport_layer tl; + + union vpx_sockaddr_x local_addr, + remote_addr; + }; + + /* + vpx_net_init() + Performs any necessary system dependent network initialization. + Should be called before any other vpx_network function. + Return: TC_OK on success, TC_ERROR otherwise + */ + TCRV vpx_net_init(); + + /* + vpx_net_destroy() + Performs any necessary system dependent network deinitialization + */ + void vpx_net_destroy(); + + /* + vpx_net_set_loglevel + Sets the log level for this library + */ + void vpx_net_set_loglevel(tc32 level); + + + /* + vpx_net_open(struct vpxsocket* vpx_sock, enum network_layer net_layer, + enum transport_layer trans_layer) + vpx_sock - pointer to an vpxsocket structure that is to hold network info + net_layer - network layer of the socket to be created + trans_layer - transport layer of the socket to be created + Attempts to create a socket with the specified network + and transport layer. Read and send timeouts default to vpx_NET_NO_TIMEOUT. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL or if the network/transport + layer is not supported. + TC_ERROR: if a socket could not be created. + */ + TCRV vpx_net_open(struct vpxsocket *vpx_sock, + enum network_layer net_layer, + enum transport_layer trans_layer); + + /* + vpx_net_close(struct vpxsocket* vpx_sock) + vpx_sock - pointer to an vpxsocket structure which + holds the socket to be closed + Attempts to close the socket associated with the vpx_sock structure + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL + TC_ERROR: if the socket could not be closed + */ + TCRV vpx_net_close(struct vpxsocket *vpx_sock); + + /* + vpx_net_bind(struct vpxsocket* vpx_sock, + union vpx_sockaddr_x* vpx_sa_x, + tcu16 port) + vpx_sock - pointer to an vpxsocket structure that contains + the socket to be bound + vpx_sa_x - pointer to an vpx_sockaddr_x struct that contains the + interface to bind to or NULL if the user wants to + bind to any interface. + port - the port to bind the socket to + Attempts to bind vpx_sock to port on the interface specified in vpx_sa_x + or to any interface if vpx_sa_x is NULL. If provided vpx_sa_x should have + been filled out by vpx_net_get_addr_info. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL or does not point to a structure + that was initialized via vpx_net_open + TC_ERROR: if vpx_sock could not be bound to the specified port + and interface + */ + TCRV vpx_net_bind(struct vpxsocket *vpx_sock, + union vpx_sockaddr_x *vpx_sa_x, + tcu16 port); + + /* + vpx_net_listen(struct vpxsocket* vpx_sock, tc32 backlog) + vpx_sock - pointer to a properly initialized and bound vpxsocket + structure to be setup to listen for incoming connections + backlog - the maximum length the queue of pending connections can grow to. + If the value is less than 1, backlog will be set to the system's + maximum value. + Attempts to put vpx_sock into a state where it can accept incoming connections. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, not properly initialized and bound, + or if the transport layer does not support listening + TC_ERROR: if the vpx_sock could not be put into the listening state + */ + TCRV vpx_net_listen(struct vpxsocket *vpx_sock, tc32 backlog); + + /* + vpx_net_accept(struct vpxsocket* vpx_sock, struct vpxsocket* vpx_sock_peer) + vpx_sock - pointer to a properly initialized, bound and listening + vpxsocket structure + vpx_sock_peer - result parameter; pointer to an vpxsocket structure that + will be filled out with the accepted connection's info + Attempts to have vpx_sock accept connections on the port it's currently + listening. If successful, vpx_sock_peer will be filled out with the + remote peer's info. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not put into the listening + state via vpx_net_listen or vpx_sock_peer is NULL + TC_ERROR: if there was an error accepting connections on vpx_sock + */ + TCRV vpx_net_accept(struct vpxsocket *vpx_sock, struct vpxsocket *vpx_sock_peer); + + /* + vpx_net_connect(struct vpxsocket* vpx_sock, tc8* ip_addr, tcu16 port) + vpx_sock - pointer to an vpxsocket structure that is to be connencted + to the endpoint described by ip_addr and port + ip_addr - pointer to a character string that contains the address to + attempt to connect to + port - the port to attempt to connect to on ip_addr + Attempt to connect vpx_sock to port on ip_addr. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, not properly initialized or + if ip_addr is NULL + TC_ERROR: if information could not be obtained about the host or + if a connection could not be established + */ + TCRV vpx_net_connect(struct vpxsocket *vpx_sock, + tc8 *ip_addr, + tcu16 port); + + /* + vpx_net_get_addr_info(tc8* ip_addr, + tcu16 port, + enum network_layer net_layer, + enum transport_layer trans_layer, + union vpx_sockaddr_x* vpx_sa_x) + ip_addr - address to resolve + port - port on ip_addr to obtain information for or 0 indicating any + net_layer - network layer desired on the host machine + trans_layer - the transport layer desired on the host machine + vpx_sa_x - pointer to an vpx_sockaddr_x union that will receive host + information if obtained + Attempts to acquire information about ip_addr that can be used in a + connection attempt. This information will be stored in vpx_sa_x which + can be used in subsequent vpx_net_ functions + Return: + TC_OK: on success + TC_INVALID_PARAMS: if ip_addr or vpx_sa_x are NULL or if the specified + network/transport layers are unsupported by this library + TC_ERROR: if no information could be obtained about the host + */ + TCRV vpx_net_get_addr_info(tc8 *ip_addr, + tcu16 port, + enum network_layer net_layer, + enum transport_layer trans_layer, + union vpx_sockaddr_x *vpx_sa_x); + + /* + vpx_net_read(struct vpxsocket* vpx_sock, tc8* buffer, + tc32 buf_len, tc32* bytes_read) + vpx_sock - pointer to a properly initialized vpxsocket structure + buffer - pointer to a character array where data is to be stored + buf_len - the max max amount of data to be read into buffer + bytes_read - pointer to an integer that will receive the actual amount + of data read or NULL + Attempts to read at most buf_len bytes off the socket into buffer. This + operation can only be done on a connected socket. If a read timeout has + been set to a non-zero value the operation will fail if it could not be + completed within the specified time. If the read timeout has been set to + 0 the operation will fail if it could not be completed immediately. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized + via vpx_net_open, was not connected via + vpx_net_connect, buffer is NULL or buf_len is <= 0 + TC_TIMEDOUT: if a read timeout has been set to non-zero value and the + operation could not be completed in the specified time + TC_WOULDBLOCK: if the read timeout has been set to 0 and the operation + could not be completed immediately + TC_ERROR: if an error other than timed out or would block is encountered + trying to complete the operation, more information can be + obtained through calling vpx_net_get_error + */ + TCRV vpx_net_read(struct vpxsocket *vpx_sock, tc8 *buffer, + tc32 buf_len, tc32 *bytes_read); + + /* + vpx_net_recvfrom(struct vpxsocket* vpx_sock, tc8* buffer, tc32 buf_len, + tc32* bytes_read, union vpx_sockaddr_x* vpx_sa_x) + vpx_sock - pointer to a properly initialized vpxsocket structure + buffer - pointer to a character array where data is to be stored + buf_len - max amount of data to be read + bytes_read - pointer to an integer that will receive the actual amount + of data read or NULL + vpx_sa_from - pointer to a vpx_sockaddr_x union used to store the address + of the remote peer the data was received from or NULL + Attempts to read at most buf_len bytes off the socket into buffer. This + operation can be done on a connected or unconnected socket. If a + read timeout has been set to a non-zero value the operation will fail if + it could not be completed within the specified time. If the send timeout + has been set to 0 the operation will fail if it could not be completed + immediately. If data is received and vpx_sa_from is non-NULL the address + of the sender will be stored in it. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized + via vpx_net_open, buffer is NULL or buf_len is <= 0 + TC_TIMEDOUT: if a read timeout has been set to non-zero value and the + operation could not be completed in the specified time + TC_WOULDBLOCK: if the read timeout has been set to 0 and the operation + could not be completed immediately + TC_ERROR: if an error other than timed out or would block is encountered + trying to complete the operation, more information can be + obtained through calling vpx_net_get_error + */ + TCRV vpx_net_recvfrom(struct vpxsocket *vpx_sock, tc8 *buffer, tc32 buf_len, + tc32 *bytes_read, union vpx_sockaddr_x *vpx_sa_from); + + /* + vpx_net_send(struct vpxsocket* vpx_sock, tc8* buffer, + tc32 buf_len, tc32* bytes_sent) + vpx_sock - pointer to a properly initialized vpxsocket structure + buffer - pointer to a character array containing data to be sent + buf_len - the length of the data in buffer + bytes_sent - pointer to an integer that will receive the actual amount + of data sent or NULL + Attempts to send buffer to vpx_sock's connected peer. If a send timeout + has been set to a non-zero value the operation will fail if it could not + be completed within the specified time. If the send timeout has been set + to 0 the operation will fail if it could not be completed immediately. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized + via vpx_net_open, was not connected via + vpx_net_connect, buffer is NULL or buf_len is <= 0 + TC_TIMEDOUT: if a send timeout has been set to non-zero value and the + operation could not be completed in the specified time + TC_WOULDBLOCK: if the send timeout has been set to 0 and the operation + could not be completed immediately + TC_ERROR: if an error other than timed out or would block is encountered + trying to complete the operation, more information can be + obtained through calling vpx_net_get_error + */ + TCRV vpx_net_send(struct vpxsocket *vpx_sock, tc8 *buffer, + tc32 buf_len, tc32 *bytes_sent); + + /* + vpx_net_sendto(struct vpxsocket* vpx_sock, tc8* buffer, tc32 buf_len, + tc32* bytes_sent, union vpx_sockaddr_x vpx_sa_to) + vpx_sock - pointer to a properly initialized vpxsocket structure + buffer - pointer to a character array containing data to be sent + buf_len - the length of the data in buffer + bytes_sent - pointer to an integer that will receive the actual amount + of data sent or NULL + vpx_sa_to - vpx_sockaddr_x containing the address of the target + Attempts to send buffer to vpx_sockaddr_to. This operation can be done on + connected and unconnected sockets. If a send timeout has been set + to a non-zero value the operation will fail if it could not be completed + within the specified time. If the send timeout has been set to 0 the + operation will fail if it could not be completed immediately. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized + via vpx_net_open, buffer is NULL or buf_len is <= 0 + TC_TIMEDOUT: if a send timeout has been set to non-zero value and the + operation could not be completed in the specified time + TC_WOULDBLOCK: if the send timeout has been set to 0 and the operation + could not be completed immediately + TC_ERROR: if an error other than timed out or would block is encountered + trying to complete the operation, more information can be + obtained through calling vpx_net_get_error + */ + TCRV vpx_net_sendto(struct vpxsocket *vpx_sock, tc8 *buffer, tc32 buf_len, + tc32 *bytes_sent, union vpx_sockaddr_x vpx_sa_to); + + /* + vpx_net_is_readable(struct vpxsocket* vpx_sock) + vpx_sock - pointer to a properly initialized vpxsocket structure to + be polled to see if data can be read from it + Return: + 0: vpx_sock was NULL, did not point to a vpxsocket structure that was + initialized via vpx_net_open or the socket has no data that can be read + 1: the socket has data that can be read + */ + tc32 vpx_net_is_readable(struct vpxsocket *vpx_sock); + + /* + vpx_net_amount_readable(struct vpxsocket* vpx_sock, TCRV* rv) + vpx_sock - pointer to a properly initialized vpxsocket structure to + be polled to see if data can be read from it + rv - TCRV pointer to receive the result of the function. This parameter + may be NULL. rv will be set to TC_OK on success and TC_ERROR + on error. + Return: + The amount of data in bytes able to be read off the socket + */ + tc32 vpx_net_amount_readable(struct vpxsocket *vpx_sock, TCRV *rv); + + /* + vpx_net_is_writeable(struct vpxsocket* vpx_sock) + vpx_sock - pointer to a properly initialized vpxsocket structure to + be polled to see if data can be written to it + Return: + 0: vpx_sock was NULL, did not point to a vpxsocket structure that was + initialized via vpx_net_open or the socket cannot be written to + without blocking + 1: the socket can be written to without blocking + */ + tc32 vpx_net_is_writeable(struct vpxsocket *vpx_sock); + + /* + vpx_net_set_read_timeout(struct vpxsocket* vpx_sock, tcu32 read_timeout) + vpx_sock - pointer to a properly initialized vpxsocket structure + read_timeout - time to wait in milliseconds before giving up on a read + operation. 0 indicates that a non-blocking attempt to read + should be made. vpx_NET_NO_TIMEOUT - indicates the socket + should never timeout. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock was NULL or did not point to an vpxsocket + that was initialized via vpx_net_open + */ + TCRV vpx_net_set_read_timeout(struct vpxsocket *vpx_sock, tcu32 read_timeout); + + /* + vpx_net_set_send_timeout(struct vpxsocket* vpx_sock, tcu32 send_timeout) + vpx_sock - pointer to a properly initialized vpxsocket structure + read_timeout - time to wait in milliseconds before giving up on a send + operation. 0 indicates that a non-blocking attempt to send + should be made. vpx_NET_NO_TIMEOUT - indicates the socket + should never timeout. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock was NULL or did not point to an vpxsocket + that was initialized via vpx_net_open + */ + TCRV vpx_net_set_send_timeout(struct vpxsocket *vpx_sock, tcu32 send_timeout); + + /* + vpx_net_get_error(tc32* vpx_net_errno) + vpx_net_errno - pointer to a tc32 to store the last system network + error code or NULL if the user does not want it + Return: + A string representing the last system network error that occurred. This + string can only be used until the next call to vpx_net_get_error() + */ + tc8 *vpx_net_get_error(tc32 *vpx_net_errno); + + /* + vpx_net_recv_buf(struct vpxsocket* vpx_sock, tc8 set, tc32* value) + vpx_sock - a pointer to a properly initialized vpxsocket structure + set - Value indicating whether the option should be set or queried. + 1 indicates the option should be set using the value stored + in value. 0 indicates the current value of the option should + be returned in value. + value - depending on the value of set, either contains the size + to set the socket's receive buffer to or will receive the + current size of the socket's receive buffer + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, wasn't initialized via + vpx_net_open or value is NULL + TC_ERROR: if the option could not be queried/set + */ + TCRV vpx_net_recv_buf(struct vpxsocket *vpx_sock, tc8 set, tc32 *value); + + /* + vpx_net_send_buf(struct vpxsocket* vpx_sock, tc8 set, tc32* value) + vpx_sock - a pointer to a properly initialized vpxsocket structure + set - Value indicating whether the option should be set or queried. + 1 indicates the option should be set using the value stored + in value. 0 indicates the current value of the option should + be returned in value. + value - depending on the value of set, either contains the size + to set the socket's send buffer to or will receive the + current size of the socket's send buffer + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, wasn't initialized via + vpx_net_open or value is NULL + TC_ERROR: if the option could not be queried/set + */ + TCRV vpx_net_send_buf(struct vpxsocket *vpx_sock, tc8 set, tc32 *value); + + /* + vpx_net_reuse_addr(struct vpxsocket* vpx_sock, tc8 set, tc32* value) + vpx_sock - a pointer to a properly initialized vpxsocket structure + set - Value indicating whether the option should be set or queried. + 1 indicates the option should be set using the value stored + in value. 0 indicates the current value of the option should + be returned in value. + value - depending on the value of set, either contains an integer + 0/1 to indicate whether the socket's reuse address option + should be turned on or off or will receive the current setting + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, wasn't initialized via + vpx_net_open or value is NULL + TC_ERROR: if the option could not be queried/set + */ + TCRV vpx_net_reuse_addr(struct vpxsocket *vpx_sock, tc8 set, tc32 *value); + + /* + vpx_net_linger(struct vpxsocket* vpx_sock, tc8 set, tcu16* on, tcu16* sec) + vpx_sock - a pointer to a properly initialized vpxsocket structure + set - Value indicating whether the option should be set or queried. + 1 indicates the option should be set using the values stored in + on and sec. 0 indicates the current value of the option should + be returned in on and sec. + on - depending on the value of set indicates whether to turn on/off (1/0) + the linger option or will receive the current setting + sec - depending on the value of set indicates the amount of time in + seconds for the socket to linger or will receive the current value + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, wasn't initialized via + vpx_net_open or on or sec are NULL + TC_ERROR: if the option could not be queried/set + */ + TCRV vpx_net_linger(struct vpxsocket *vpx_sock, tc8 set, tcu16 *on, tcu16 *sec); + + /* + vpx_net_multicast_ttl(struct vpxsocket* vpx_sock, tc8 set, tcu8* value) + vpx_sock - pointer to a properly initialized vpxsocket structure + set - flag indicating whether to set (non-zero value) or + query (0) the option + value - depending on the value of set, sets the ttl to the value stored + in value or receives the current value of ttl + Attempts to set/query the multicast ttl value of the socket + represented by vpx_sock + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not initialized via + vpx_net_open, the socket is not a udp socket or + value is NULL + TC_ERROR: if the option could not be queried/set + */ + TCRV vpx_net_multicast_ttl(struct vpxsocket *vpx_sock, tc8 set, tcu8 *value); + + + /* + vpx_net_join_multicast(struct vpxsocket* vpx_sock, + union vpx_sockaddr_x* remote_addr) + vpx_sock - pointer to a properly initialized vpxsocket structure + local_addr - an vpx_sockaddr_x structure containing the address of the + local interface to use for the multicast session or NULL + indicating any interface can be used. (Currently ignored + for IPv6). + remote_addr - an vpx_sockaddr_x structure containing the multicast address + Attempts to add vpx_sock to the multicast session indicated by remote_addr. + On success the reuse_addr option will be set on vpx_sock so others may + join the session and vpx_sock will be added to the session. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized via + vpx_net_open, vpx_sock does not represent a UDP socket, + or remote_addr is NULL + TC_ERROR: if the reuse_addr option could not be set or the socket could + not be added to the multicast session + */ + TCRV vpx_net_join_multicast(struct vpxsocket *vpx_sock, + union vpx_sockaddr_x *local_addr, + union vpx_sockaddr_x *remote_addr); + + /* + vpx_net_join_multicast_addr(struct vpxsocket* vpx_sock + , tc8* ip_addr + , tcu16 port) + vpx_sock - pointer to an vpxsocket structure that is to be connencted + to the endpoint described by ip_addr and port + ip_addr - pointer to a character string that contains the multicast + address to attempt to join + port - the port to attempt to join on ip_addr + Attempt to join vpx_sock to port on ip_addr. + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized via + vpx_net_open, vpx_sock does not represent a UDP socket, + or remote_addr is NULL + TC_ERROR: if the reuse_addr option could not be set or the socket could + not be added to the multicast session + */ + TCRV vpx_net_join_multicast_addr(struct vpxsocket *vpx_sock + , tc8 *ip_addr + , tcu16 port); + + + /* + vpx_net_leave_multicast(struct vpxsocket* vpx_sock) + vpx_sock - pointer to a properly initialized vpxsocket structure to be + removed from the multicast session + Attempts to remove vpx_sock from the multicast session it was previously + added to via vpx_net_join_multicast + Return: + TC_OK: on success + TC_INVALID_PARAMS: if vpx_sock is NULL, was not properly initialized via + vpx_net_open or the socket is not a UDP socket + TC_ERROR: if the socket could not be removed from the session + */ + TCRV vpx_net_leave_multicast(struct vpxsocket *vpx_sock); + +#else //!defined(vpx_NET_STUBS) + struct vpxsocket + { + int dummy; + }; + +# define vpx_net_init() 0 +# define vpx_net_destroy() 0 +# define vpx_net_set_loglevel(l) + tc8 *vpx_net_get_error(tc32 *vpx_net_errno); + +#endif + +#if defined(__cplusplus) +} +#endif + +#endif //__vpx_NETWORK_H__